Delphi作為一種成熟的開發工具,在構建REST API和多層架構應用方面也提供了豐富的支援。本文將會涵蓋WebBroker、DataSnap和RAD Server等關鍵技術,並結合程式碼範例說明如何運用這些技術構建高效能的後端服務。首先,我們會介紹如何使用WebBroker技術來建立REST API,並示範如何處理HTTP請求、從請求中提取引數值、建立REST客戶端,以及如何將JSON字串轉換為Delphi記錄。接著,我們將探討DataSnap框架,從其發展歷程到伺服器的建立步驟、核心元件的應用,以及伺服器方法的實作,都會有詳細的說明。最後,我們將會介紹RAD Server,一個更現代化的REST API發布框架,並透過實際範例展示如何建立RAD Server資源和對應的客戶端應用程式,讓開發者能夠快速上手並構建功能完善的後端服務。
使用WebBroker建立REST API與客戶端實作
WebBroker技術實作REST API
在建立REST API的過程中,我們使用了WebBroker技術來處理不同的HTTP請求。以下是一個範例程式碼,展示瞭如何使用TWebActionItem來處理請求:
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
AJtw: TJsonTextWriter;
ASw: TStringWriter;
AToDo: TToDo;
begin
AJtw := TJsonTextWriter.Create(ASw);
try
// 處理請求邏輯
WriteItem(AToDo, AJtw);
AJtw.WriteEndObject;
Response.Content := ASw.ToString;
finally
AJtw.Free;
ASw.Free;
end;
end;
內容解密:
TJsonTextWriter用於將資料寫入JSON格式。TStringWriter用於捕捉TJsonTextWriter的輸出結果。WriteItem程式負責將TToDo記錄寫入JSON格式。- 使用
try...finally區塊確保資源正確釋放。
更新Web Action以支援多種操作
為了支援多種操作(如新增、更新、刪除),我們需要在Web Action中提取引數值並呼叫相應的資料模組方法。以下是一個更新後的Web Action範例:
// 更新操作範例
procedure TWebModule1.UpdateToDoAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
var
Id, Title, Category: string;
begin
Id := Request.QueryFields.Values['Id'];
Title := Request.QueryFields.Values['Title'];
Category := Request.QueryFields.Values['Category'];
// 呼叫資料模組的ToDoUpdate方法
GetToDoData.ToDoUpdate(StrToInt(Id), Title, Category);
Response.Content := 'Update successful';
end;
內容解密:
- 從
Request.QueryFields中提取引數值。 - 呼叫
GetToDoData.ToDoUpdate方法更新資料。 - 傳回更新成功的訊息。
建立REST客戶端
為了與REST API互動,我們需要在客戶端專案中建立一個新的資料模組,並加入必要的REST元件(如TRESTClient、TRESTResponse、TRESTRequest)。以下是一個設定REST客戶端的範例:
// 設定REST客戶端元件
procedure TDMToDoWebBrokREST.SetupRESTClient;
begin
RClientToDo.BaseURL := 'http://127.0.0.1:8080';
RReqToDoList.Resource := 'ToDo/List';
// 設定其他REST請求元件的屬性
end;
內容解密:
- 設定
TRESTClient的BaseURL屬性為伺服器的URL。 - 設定各個
TRESTRequest元件的Resource屬性和引數。
實作資料模組方法
在資料模組中,我們需要實作與REST API互動的方法。以下是一個範例,展示瞭如何實作ToDoCreate方法:
function TDMToDoWebBrokREST.ToDoCreate(AValue: TToDo): Integer;
begin
RReqToDoCreate.Params[0].Value := AValue.Title;
RReqToDoCreate.Params[1].Value := AValue.Category;
RReqToDoCreate.Execute;
Result := RRespToDo.Content.ToInteger;
end;
內容解密:
- 設定
RReqToDoCreate元件的引數值。 - 執行
RReqToDoCreate請求。 - 傳回伺服器的回應內容。
JSON字串轉換為TToDo記錄
為了處理伺服器傳回的JSON資料,我們需要實作一個將JSON字串轉換為TToDo記錄的函式。以下是一個範例:
function StrToToDo(S: string): TToDo;
var
ASr: TStringReader;
AJtr: TJsonTextReader;
begin
ASr := TStringReader.Create(S);
try
AJtr := TJsonTextReader.Create(ASr);
try
while AJtr.Read do
if AJtr.TokenType = TJsonToken.StartObject then
begin
Result.Id := StrToInt(ReadStr(AJtr));
Result.Title := ReadStr(AJtr);
Result.Category := ReadStr(AJtr);
end;
finally
AJtr.Free;
end;
finally
ASr.Free;
end;
end;
內容解密:
- 使用
TStringReader和TJsonTextReader讀取JSON字串。 - 解析JSON內容並指定給
TToDo記錄的欄位。 - 確保資源正確釋放。
使用DataSnap建立伺服器
WebBroker適合基本的HTTP伺服器功能,但如果要建立更複雜的系統,則需要使用完整的Delphi多層架構,如DataSnap或RAD Server。這些框架提供了比WebBroker更多的高階功能。
DataSnap框架簡介
DataSnap框架自Delphi早期版本就已存在,並隨著時間的推移不斷演進。Delphi 3引入了MIDAS技術,使建立客戶端/伺服器資料函式庫應用程式變得更加容易。在Delphi 6中,該技術被重新命名為DataSnap,而在Delphi 2009中,它被完全重寫。在新的架構中,由DataSnap伺服器釋出的遠端方法看起來像典型的SQL關聯式資料函式庫系統所公開的資料函式庫儲存程式。
建立DataSnap伺服器
在Delphi中開啟「新建專案」對話方塊時,DataSnap伺服器類別中有三個不同的精靈可供選擇:
- DataSnap REST應用程式精靈:產生一個DataSnap網頁伺服器專案,包括網頁和JavaScript檔案,以便從瀏覽器呼叫伺服器方法。
- DataSnap伺服器精靈:支援建立舊式的根據DBX的DataSnap伺服器,不建議使用。
- DataSnap WebBroker應用程式精靈:產生一個託管在WebBroker網頁伺服器中的DataSnap伺服器應用程式。
建立DataSnap伺服器的步驟
- 選擇DataSnap WebBroker應用程式精靈並點選「確定」。
- 在精靈的第一頁,勾選新增Linux支援的選項。
- 在第三個標籤頁中,選擇要新增到新專案的功能,並點選「下一步」。
- 在下一個標籤頁中,選擇伺服器方法類別的基底類別。對於簡單的專案,可以選擇TComponent或TDataModule。
- 在最後一個標籤頁中,指定專案檔案的資料夾。資料夾名稱需要是Delphi專案的有效識別碼。
DataSnap伺服器的核心元件
DataSnap伺服器應用程式的核心是DSServer1元件,位於ServerContainerUnit1中。該元件具有AutoStart屬性,預設為True,因此在應用程式啟動時,網頁伺服器開始等待來自客戶端的請求。
實作DataSnap伺服器功能
在DataSnap架構中,程式設計師不需要寫程式碼來例項化伺服器方法類別。DSServerClass1元件的LifeCycle屬性控制伺服器類別的生命週期。該屬性可以有三個不同的值:
- 預設值為Session,表示每個連線的客戶端都有一個伺服器方法類別例項。
procedure TServerContainer3.DSServerClass1GetClass(
DSServerClass: TDSServerClass;
var PersistentClass: TPersistentClass);
begin
PersistentClass := TServerMethods3;
end;
內容解密:
這段程式碼定義了TServerContainer3類別中的DSServerClass1GetClass方法,用於指定與DSServerClass1元件相關聯的伺服器方法類別。在這個方法中,將PersistentClass引數設定為TServerMethods3類別的參照。這意味著當客戶端連線到DataSnap伺服器時,TServerMethods3類別將被用來處理客戶端的請求。
DataSnap伺服器的建置區塊
ServerMethodsUnit1包含了伺服器的實際API。該類別中宣告的所有公開和釋出的方法都可以被客戶端呼叫。DSServerClass1元件連線了DSServer1元件和伺服器方法類別。
使用DataSnap建立行動後端服務
DataSnap是一種強大的技術,能夠幫助開發者建立可擴充套件且靈活的後端服務,支援多種客戶端應用程式。在本章中,我們將探討如何使用DataSnap建立一個簡單的後端服務,並為其建立客戶端應用程式。
建立DataSnap伺服器
首先,我們需要建立一個DataSnap伺服器。DataSnap伺服器可以以多種形式存在,例如WebBroker網路伺服器、主控台應用程式、VCL或FireMonkey表單應用程式,或是Windows服務。
在建立DataSnap伺服器時,我們需要考慮LifeCycle屬性的設定。該屬性決定了伺服器類別例項的生命週期,有三種可能的取值:
Server:在這種模式下,所有連線的客戶端分享同一個伺服器類別例項。因此,伺服器方法的實作必須是執行緒安全的,因為其方法可能會被不同的執行緒同時呼叫。Session:在這種模式下,每個客戶端會話都有自己的伺服器類別例項。Invocation:這是最具擴充套件性的選項。在這種模式下,伺服器方法類別只在伺服器方法呼叫期間例項化,不會在多次呼叫之間保持狀態。
TServerMethods3類別
在本例中,DataSnap伺服器精靈已經為我們生成了TServerMethods3類別,其中包含兩個範例伺服器方法:EchoString和ReverseString。這些方法接受一個字串引數並傳回一個字串。您可以根據需要新增其他公共方法到這個類別中,它們將自動對客戶端可用。
type
{$METHODINFO ON}
TServerMethods3 = class(TDataModule)
private
{ Private declarations }
public
{ Public declarations }
function EchoString(Value: string): string;
function ReverseString(Value: string): string;
end;
{$METHODINFO OFF}
DataSnap伺服器的特點
DataSnap伺服器通常與其宿主環境無關,這意味著它們可以被實作為各種形式的應用程式。伺服器精靈生成的TServerMethods3類別位於ServerMethodsUnit1單元中。
為DataSnap伺服器建立客戶端應用程式
要建立DataSnap客戶端應用程式,我們需要生成一個客戶端存取單元,該單元模擬了伺服器上可用的功能。DataSnap代理生成器需要存取正在執行的伺服器,並查詢伺服器以生成DataSnap客戶端類別,該類別公開了與伺服器上相同的方法。
建立DataSnap客戶端的步驟
- 首先,執行DataSnap伺服器,並在主控台視窗中輸入
start以啟動伺服器。 - 在專案管理員中右鍵點選專案群組節點,並選擇「新增專案」。
- 建立一個新的Delphi多裝置空白應用程式,並將主要表單單元另存為
uFormDSClient,將專案另存為DataSnapClient。 - 使用DataSnap REST客戶端模組精靈生成DataSnap客戶端程式碼。該精靈位於「DataSnap伺服器」類別下的「新增專案」對話方塊中。
- 在精靈的第一個畫面上,選擇「遠端伺服器」作為伺服器位置。
- 在第二個畫面上,指定DataSnap伺服器專案型別。
- 在最後一個畫面上,輸入伺服器的URL(或主機名稱)並測試連線。
DataSnap客戶端類別
精靈生成的TServerMethods3Client類別具有與伺服器方法類別相同的名稱,但附加了「Client」字尾。它繼承自TDSAdminRestClient類別,並具有與伺服器相同的函式宣告。
type
TServerMethods3Client = class(TDSAdminRestClient)
private
FEchoStringCommand: TDSRestCommand;
FReverseStringCommand: TDSRestCommand;
public
constructor Create(ARestConnection: TDSRestConnection); overload;
constructor Create(ARestConnection: TDSRestConnection; AInstanceOwner: Boolean); overload;
destructor Destroy; override;
function EchoString(Value: string; const ARequestFilter: string = ''): string;
function ReverseString(Value: string; const ARequestFilter: string = ''): string;
end;
連線DataSnap伺服器
在客戶端模組單元中,有一個DSRESTConnection1元件已經被新增。在其主機和埠屬性中,輸入了在精靈最後一頁中輸入的值。右鍵點選連線元件並選擇「測試連線」以驗證伺服器是否仍在執行。
使用RAD Server建立簡易REST API
在前一章中,我們探討了使用Delphi建立行動後端的不同選項,並深入瞭解了DataSnap架構。雖然DataSnap是一個強大且豐富的框架,但它並不是為現代REST風格的API設計的。若要完全採用REST模型,建立無狀態且可擴充套件的架構,並使用更多開箱即用的服務,建議使用Embarcadero為Delphi新增的RAD Server架構。本章將介紹RAD Server的核心元素,並透過實際範例來展示如何建立RAD Server資源及客戶端應用程式。
RAD Server技術需求
RAD Server是Delphi Enterprise或Architect版本的一部分。它被設計為一個可擴充套件的REST API發布框架。其功能透過建立Delphi BPL套件進行擴充套件,這些套件在RAD Server啟動時被載入。同時,它需要存取Embarcadero InterBase SQL資料函式庫,用於儲存其系統資料函式庫。在安裝Delphi時,請務必安裝InterBase的開發版本,它附帶了特殊的授權,用於作為RAD Server的系統資料函式庫。
13.1 RAD Server設定
首先,我們需要正確設定RAD Server。請確保您已安裝Delphi Enterprise或Architect版本,並在安裝過程中選擇了InterBase開發版本。
建立RAD Server資源
RAD Server的功能透過建立Delphi BPL套件進行擴充套件,這些套件包含自訂的資源和端點。讓我們開始建立一個簡單的ToDo列表資源。
建立ToDo列表資源
- 開啟Delphi並建立一個新的RAD Server資源專案。
- 定義ToDo列表資源的端點,例如取得所有ToDo專案、新增ToDo專案、更新ToDo專案和刪除ToDo專案。
// ToDoResource.pas
unit ToDoResource;
interface
uses
System.SysUtils, System.Classes, EMS.ResourceAPI, EMS.ResourceTypes;
type
[ResourceName('todo')]
TToDoResource = class(TDataModule)
published
[EndpointName('GetAllToDos')]
procedure GetAllToDos(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
[EndpointName('AddToDo')]
procedure AddToDo(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
[EndpointName('UpdateToDo')]
procedure UpdateToDo(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
[EndpointName('DeleteToDo')]
procedure DeleteToDo(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
end;
implementation
uses
System.JSON;
procedure TToDoResource.GetAllToDos(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
LJSON: TJSONArray;
begin
// 從資料函式庫取得所有ToDo專案並轉換為JSON陣列
LJSON := GetToDosFromDatabase;
AResponse.Body.SetValue(LJSON, True);
end;
procedure TToDoResource.AddToDo(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
LJSON: TJSONObject;
begin
// 從請求中解析JSON資料並新增ToDo專案到資料函式庫
LJSON := ARequest.Body.GetValue<TJSONObject>;
AddToDoToDatabase(LJSON);
AResponse.StatusCode := 201;
end;
procedure TToDoResource.UpdateToDo(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
LJSON: TJSONObject;
begin
// 從請求中解析JSON資料並更新ToDo專案在資料函式庫中
LJSON := ARequest.Body.GetValue<TJSONObject>;
UpdateToDoInDatabase(LJSON);
AResponse.StatusCode := 200;
end;
procedure TToDoResource.DeleteToDo(const AContext: TEndpointContext; const ARequest: TEndpointRequest; const AResponse: TEndpointResponse);
var
LID: Integer;
begin
// 從請求中解析ToDo專案的ID並從資料函式庫中刪除
LID := StrToInt(ARequest.Params['id'].Value);
DeleteToDoFromDatabase(LID);
AResponse.StatusCode := 204;
end;
end.
內容解密:
TToDoResource類別定義了一個名為todo的資源,包含四個端點:GetAllToDos、AddToDo、UpdateToDo和DeleteToDo。- 每個端點對應一個HTTP請求,用於操作ToDo列表。
GetAllToDos用於取得所有ToDo專案,傳回一個JSON陣列。AddToDo用於新增一個ToDo專案,接收一個JSON物件作為請求體。UpdateToDo用於更新一個現有的ToDo專案,同樣接收一個JSON物件。DeleteToDo用於刪除一個指定的ToDo專案,根據請求引數中的ID進行刪除。
建立客戶端應用程式
接下來,我們將建立一個簡單的客戶端應用程式,用於與我們的RAD Server ToDo列表資源互動。
- 在Delphi中建立一個新的多裝置應用程式專案。
- 新增按鈕和編輯框,用於與使用者互動。
- 使用REST Client Library來呼叫RAD Server上的端點。
// MainForm.pas
unit MainForm;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
REST.Client, Data.Bind.Components, Data.Bind.ObjectScope;
type
TForm1 = class(TForm)
btnGetAllToDos: TButton;
btnAddToDo: TButton;
edtToDo: TEdit;
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
RESTResponse1: TRESTResponse;
procedure btnGetAllToDosClick(Sender: TObject);
procedure btnAddToDoClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.btnGetAllToDosClick(Sender: TObject);
begin
RESTClient1.BaseURL := 'http://your-rad-server-url.com/todo';
RESTRequest1.Method := rmGET;
RESTRequest1.Execute;
ShowMessage(RESTResponse1.Content);
end;
procedure TForm1.btnAddToDoClick(Sender: TObject);
var
LJSON: TJSONObject;
begin
LJSON := TJSONObject.Create;
try
LJSON.AddPair('todo', edtToDo.Text);
RESTClient1.BaseURL := 'http://your-rad-server-url.com/todo';
RESTRequest1.Method := rmPOST;
RESTRequest1.Body.JSONWriter.WriteObject(LJSON);
RESTRequest1.Execute;
finally
LJSON.Free;
end;
end;
end.
內容解密:
TForm1類別代表客戶端應用程式的主表單,包含兩個按鈕和一個編輯框。btnGetAllToDosClick事件處理函式用於呼叫GetAllToDos端點,取得所有ToDo專案。btnAddToDoClick事件處理函式用於呼叫AddToDo端點,新增一個新的ToDo專案。- 使用了REST Client Library中的元件,如
TRESTClient、TRESTRequest和TRESTResponse,來傳送HTTP請求和處理回應。