Archive for category C++Builder XE程式設計

使用DataSnap XE開發分散式, 精簡型(Thin Client) 應用系統研討會投影片和範例

上星期在台北, 新竹和高雄做了3場有關DataSnap XE技術的研討會, 回台北之後就感冒了, 晚了這麼多天才把研討會投的影片和範例放上來實在很抱歉.

由於WordPress似乎不能上傳Rar的檔案形態, 因此我把範例壓縮檔再加上.doc的副檔名, 需要的朋友下載之後再把.doc副檔名從檔案名稱移除即可.

此外本文附的範例包含了DataSnap證證客戶端的範例程式, 興德網上的範例缺少了這個範例.

這次研討會之後我可能會休息一斷時間, 不過看到這次的3場研討會參加的人數都比以前多上許多, 反應也很熱烈, 許多參加的朋友對於Delphi/BCB未來的GUI框架和Delphi 64位元版本都非常的期待, 這真是一個很好的現象, 我個人也希望EMBT好好的把握下一個版本的質量, 推出一個令Delphi/BCB使用者都振奮不已的版本. 在此同時我也靜待EMBT讓我加入Beta的測試.

DataSnap Thin Client Development

Demos.rar

 

9 則迴響

從原生API到REST API – 使用C++Builder XE開發REST應用程式

已經記不得是從什麼開始進入Windows程式設計的世界了,但這並不重要,因為我只要記得那時要寫Windows的程式就需要使用Windows API。在那個時候Windows API是用C定義的,因此當時是C/C++開發工具以及使用C/C++開發的工具最盛行的時代,原因無他,因為當時只能使用C/C++來呼叫Windows API。因此當Borland開發Turbo Pascal For Windows時,Borland需要把使用C定義的Windows API轉換為Pascal的語法,如此一來Pascal的程式碼才能夠順利的呼叫Windows API,當然,Delphi的Windows程式單元就是為了這個目的定義的。

隨著Java和.NET的流行,開發人員又面對了Java API以及.NET API,當然更別提網際網路上流行的PHP和Ruby API。麻煩的是要使用特定程式語言提供的資源或是服務,開發人員或是使用者就必須使用特定程式語言來存取,這妨礙了軟體成為虛擬資源的可能。然而隨著JSON突破了資料封裝的障礙之後,如果也能讓特定程式語言或是框架的API不再成為呼叫服務的障礙,那麼我們的確可以讓所有軟體轉換為虛擬資源,如此一來使用者就可以在任何設備上存取任何軟體提供的服務了,REST就是答案之一。

雲端技術的目的之一就是在轉換軟體服務為虛擬資源,讓用戶端能夠存取任何雲端架構中提供的服務,為了讓所有型態的用戶端能夠使用虛擬資源,使用REST做為呼叫API並且使用JSON做為封裝的機制就可以突破平台,程式語言,框架和各種設備的限制。例如Microsoft的Azure除了提供.NET的API之,也提供了REST API。這個趨勢的確是API演化的大突破,這代表了API已經從原生API轉換到跨越平台,程式語言和框架的API,使用REST API我們可以讓Delphi或是C++Builder的用戶端呼叫Java或是.NET的服務,當然我們也可以建立Delphi或是C++Builder的伺服器以提供服務給Java,.NET或是PHP,Ruby等用戶端來使用。

例如下圖是Azure API之一,

用戶端只需要使用HTTP的Get方法以及如下的URI就可以

http://myaccount.blob.core.windows.net/?comp=list

取得帳戶中的容器物件資訊。

再讓我們看看另外一個例子,我在網際網路上找到一個使用PHP撰寫的RESTful範例,在下面的URL中提供了CRUD服務:

http://labs.wso2.org/wsf/php/demo.php?name=RESTFulCRUD&demo=CRUDApplications/demo_client.php&src=./CRUDApplications

下圖是這個PHP RESTful CRUD的範例畫面:

我們可以看到,只要使用HTTP的Get方法和下面的URI:

http://labs.wso2.org/wsf/php/solutions/CRUDApplications/school_applications.php/applications

就可以取得由PHP程式語言撰寫的服務。

在沒有REST之前,要讓C/C++用戶端存取 PHP的服務似乎有一點難度,但有了REST之後,我們就可以藉由REST API把PHP撰寫的服務當成是由C/C++程式碼撰寫的服務一樣來呼叫。例如下圖是我使用C++Builder XE撰寫的用戶端,它成功的呼叫了這個PHP RESTful CRUD的範例服務,並且取得了服務的結果,從下圖當中我們可以立即看到這個PHP RESTful CRUD的範例是使用XML格式來封裝交換的資料,而並不是使用JSON格式。

那麼我們如何撰寫C/C++的應用程式來呼叫這個PHP RESTful CRUD的範例服務呢? 很簡單,C++Builder XE中提供了TDSHTTP類別可以讓程式師執行HTTP方法,在下面的Button1Click事件處理函式中我建立了一個TDSHTTP物件,然後呼叫GetResponse方法要求TDSHTTP物件向Edit1中Text特性值指定的uri發出Get方法請求:

void __fastcall TForm8::Button1Click(TObject *Sender)

{

TDSHTTP *pDSHTTP;

System::UnicodeString sResult;

System::UnicodeString url;

 

pDSHTTP = new TDSHTTP();

pDSHTTP->HTTPOptions = pDSHTTP->HTTPOptions << hoKeepOrigProtocol;

try

{

url = Edit1->Text;

sResult = GetResponse(pDSHTTP, url);

Memo1->Lines->Text = sResult;

}

__finally

{

delete pDSHTTP;

}

}

GetResponse方法在009行呼叫TDSHTTP物件的Get方法並且傳入指定的uri以及一個TMemoryStream物件以儲存執行結果。接著在011行使用TStreamReader物件呼叫它的ReadToEnd方法並且把結果以字串的型態回傳給用戶端,而這個結果就是PHP RESTful CRUD範例服務執行的結果,我們藉由C/C++程式碼,使用REST API來呼叫使用它提供的服務。

001    System::UnicodeString TForm8::GetResponse(TDSHTTP *pDSHTTP, System::UnicodeString Url)

002    {

003      TStream *pMemoryStream;

004      TStreamReader *pReader;

005

006      pMemoryStream = new TMemoryStream();

007      try

008      {

009      pDSHTTP->Get(Url, pMemoryStream);

010      pMemoryStream->Position = 0;

011      pReader = new TStreamReader(pMemoryStream, true);

012      try

013      {

014        return pReader->ReadToEnd();

015      }

016      __finally

017      {

018        delete pReader;

019      }

020      }

021      __finally

022      {

023      delete pMemoryStream;

024      }

025

026      return “";

027    }

C++Builder XE除了可以使用REST API做為用戶端存取由其他程式語言撰寫的REST伺服器之外,由於C++Builder XE也加入了RTTI的功能(終於),因此現在C++Builder XE也可以開發DataSnap伺服器,而且XE版也可以開發使用DataSnap技術的REST伺服器應用程式,讓使用C/C++撰寫的服務由其他用戶端呼叫使用。

現在就讓我們看看如何使用C++Builder XE開發一個簡單的REST伺服器。

開發C/C++ REST應用程式

要開發DataSnap REST應用程式在C++Builder XE中非常的簡單,點選File|New|Other功能表,在新的DataSnap Server專案選項中點選DataSnap REST Application圖像即可,如下所示:

BCB XE提供三種型態的DataSnap REST應用程式,在這個範例中讓我們選擇建立可單獨執行的VCL應用程式型態做為DataSnap REST伺服器,如下所示:

點選Next按鈕之後REST精靈會詢問REST伺服器使用的HTTP通信埠,程式師也可以點選『Find Open Port』按鈕來搜尋可供使用的通信埠,內定上是使用8080。

再點選Next按鈕之後REST精靈會詢問開發人員是否需要使用安全認證功能以及是否需要REST精靈產生兩個範例輸出方法,在這裡讓我們點選Select all選擇使用所有的功能。

最後REST精靈會詢問伺服器方法使用的袓先類別,在這個範例中讓我們接受使用內定的TDSServerModule做為伺服器方法使用的袓先類別,如此一來稍後當我們在此類別中實作的任何方法都將可以自動輸出讓各種用戶端應用程式呼叫使用。

當REST精靈結束之後就會在BCB整合發展環境中建立一個新的專案,其中包含了剛才設定相關的檔案以及許多JavaScript和HTML樣版檔案,如下所示:

我們以後有機會再討論如何使用這些產生檔案,現在讓我們集中焦點先為這個REST伺服器撰寫一個服務方法,這個服務方法將從資料表中讀取資料並且使用JSON的格式傳遞回用戶端,因此任何支援JSON的用戶端都能夠使用這個使用C/C++撰寫的服務方法。

因此現在請在REST應用程式中建立一個資料模組並且放入dbExpress元件連結MS SQL Server中的範例資料表。

接著開啟專案中的ServerMethodsUnit1程式單元,在其中實作GetFishInfo方法,GetFishInfo將建立一個TJSONArray物件,呼叫AddFishData在TJSONArray物件加入資料,最後回傳TJSONArray物件回用戶端,其實作程式碼如下:

TJSONArray* TServerMethods1::GetFishInfo()

{

TJSONArray *pContainer;

 

pContainer = new TJSONArray();

AddFishData(pContainer);

return pContainer;

}

而AddFishData方法使用資料模組中的dbExpress元件從資料表中取出一筆一筆的資料,同時把其中兩個欄位的資料值加入到一個TJSONPair物件中,再呼叫TJSONArray的AddElement方法把TJSONPair物件加入到TJSONArray物件中:

void TServerMethods1::AddFishData(TJSONArray *pContainer)

{

if (!dmFishFacts->cdsFishFacts->Active)

dmFishFacts->cdsFishFacts->Active = true;

 

try

{

dmFishFacts->cdsFishFacts->First();

while (!dmFishFacts->cdsFishFacts->Eof)

{

TJSONPair *pPair = new TJSONPair(dmFishFacts->cdsFishFacts->FieldByName(“Category")->AsString, dmFishFacts->cdsFishFacts->FieldByName(“Common_Name")->AsString);

pContainer->AddElement((TJSONValue *) pPair);

dmFishFacts->cdsFishFacts->Next();

}

}

__finally

{

dmFishFacts->cdsFishFacts->Active = false;

}

}

現在編譯這個範例REST應用程式並且執行它,下圖就是範例REST應用程式執行的畫面,點選表單中的Start按鈕以啟動伺服器:

現在我們就可以點選表單中的Open Browser按鈕啟動瀏覽器來呼叫範例REST應用程式輸出的服務。

下圖是瀏覽器啟動之後顯示的畫面,由於在前面我們建立範例REST應用程式時選擇了使用安全驗證功能,因此瀏覽器顯示了登錄資訊,由此現在我們還沒有在程式碼中使用安全驗證功能,因此讓我們點選Server Functions連結:

此時瀏覽器會顯示類似如下的內容,我們可以看到範例REST應用程式中輸出服務的類別TServerMethods1輸出了三個方法,其中包含了GetFishInfo。如果我們點選下圖中的EXECUTE按鈕就可以在瀏覽器下方看到呼叫GetFishInfo方法之後C/C++ REST伺服器回傳的執行結果:

這個執行結果的格式果然是使用JSON陣列封裝資料。

由於這個C/C++伺服器也是一個REST伺服器,因此我們可以直接使用uri來存取它提供的服務。在內定C++Builder XE的REST伺服器使用如下的格式讓用戶端存取它的服務:

http(s)://伺服器/datasnap/rest/輸出類別名稱/方法名稱/方法參數

由於這個範例REST伺服器輸出的類別是TServerMethods1,而我們呼叫的方法是GetFishInfo,因此如果我們在瀏覽器的url位址欄位中輸入如下的uri:

http://localhost:8080/datasnap/rest/TServerMethods1/GetFishInfo

就可以取得範例C/C++ REST伺服器執行的結果:

果然,由C/C++程式語言撰寫的REST伺服器可以直接使用瀏覽器來呼叫,或是使用任何其他的程式語言,框架或是設備來呼叫這個C/C++ REST伺服器。

如何? C++Builder XE提供的REST和JSON功能是不是非常的強大又易於使用呢?

現在我們既然完成了這個範例C/C++ REST伺服器,那麼讓我們回到本文討論的首先範例程式,看看這個通用的REST用戶端是否能夠呼叫由C++Builder XE開發的REST伺服器。

我們只需要在C++Builder XE中載入首先範例應用程式,加入一個新的按鈕,然後讓按鈕也使用原先『呼叫PHP REST CRUD範例』按鈕的事件處理函式即可,我們甚至不需要撰寫任何新的程式碼。

編譯並且執行這個範例應用程式,然後在表單上方的Edit控制項中輸入:

http://localhost:8080/datasnap/rest/TServerMethods1/GetFishInfo

點選新加入的『呼叫BCB DataSnap REST伺服器範例』按鈕,就可以看到下面的執行結果:

果然,我們仍然可以使用TDSHTTP元件呼叫使用C/C++程式碼撰寫的REST伺服器,現在這個範例用戶端應用程式可同時呼叫由PHP和C/C++撰寫的REST伺服器,事實上這個範例應用程式可以呼叫任何的REST伺服器。

好了,這篇文章也算不短了,就讓我們暫時到此為止,有機會的話下次再談談原生C/C++ DataSnap伺服器。

 

 

 

 

 

 

 

 

 

 

 

 

 

6 則迴響