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元件(如TRESTClientTRESTResponseTRESTRequest)。以下是一個設定REST客戶端的範例:

// 設定REST客戶端元件
procedure TDMToDoWebBrokREST.SetupRESTClient;
begin
  RClientToDo.BaseURL := 'http://127.0.0.1:8080';
  RReqToDoList.Resource := 'ToDo/List';
  // 設定其他REST請求元件的屬性
end;

內容解密:

  • 設定TRESTClientBaseURL屬性為伺服器的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;

內容解密:

  • 使用TStringReaderTJsonTextReader讀取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伺服器類別中有三個不同的精靈可供選擇:

  1. DataSnap REST應用程式精靈:產生一個DataSnap網頁伺服器專案,包括網頁和JavaScript檔案,以便從瀏覽器呼叫伺服器方法。
  2. DataSnap伺服器精靈:支援建立舊式的根據DBX的DataSnap伺服器,不建議使用。
  3. DataSnap WebBroker應用程式精靈:產生一個託管在WebBroker網頁伺服器中的DataSnap伺服器應用程式。

建立DataSnap伺服器的步驟

  1. 選擇DataSnap WebBroker應用程式精靈並點選「確定」。
  2. 在精靈的第一頁,勾選新增Linux支援的選項。
  3. 在第三個標籤頁中,選擇要新增到新專案的功能,並點選「下一步」。
  4. 在下一個標籤頁中,選擇伺服器方法類別的基底類別。對於簡單的專案,可以選擇TComponent或TDataModule。
  5. 在最後一個標籤頁中,指定專案檔案的資料夾。資料夾名稱需要是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類別,其中包含兩個範例伺服器方法:EchoStringReverseString。這些方法接受一個字串引數並傳回一個字串。您可以根據需要新增其他公共方法到這個類別中,它們將自動對客戶端可用。

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客戶端的步驟

  1. 首先,執行DataSnap伺服器,並在主控台視窗中輸入start以啟動伺服器。
  2. 在專案管理員中右鍵點選專案群組節點,並選擇「新增專案」。
  3. 建立一個新的Delphi多裝置空白應用程式,並將主要表單單元另存為uFormDSClient,將專案另存為DataSnapClient
  4. 使用DataSnap REST客戶端模組精靈生成DataSnap客戶端程式碼。該精靈位於「DataSnap伺服器」類別下的「新增專案」對話方塊中。
  5. 在精靈的第一個畫面上,選擇「遠端伺服器」作為伺服器位置。
  6. 在第二個畫面上,指定DataSnap伺服器專案型別。
  7. 在最後一個畫面上,輸入伺服器的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列表資源

  1. 開啟Delphi並建立一個新的RAD Server資源專案。
  2. 定義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的資源,包含四個端點:GetAllToDosAddToDoUpdateToDoDeleteToDo
  • 每個端點對應一個HTTP請求,用於操作ToDo列表。
  • GetAllToDos用於取得所有ToDo專案,傳回一個JSON陣列。
  • AddToDo用於新增一個ToDo專案,接收一個JSON物件作為請求體。
  • UpdateToDo用於更新一個現有的ToDo專案,同樣接收一個JSON物件。
  • DeleteToDo用於刪除一個指定的ToDo專案,根據請求引數中的ID進行刪除。

建立客戶端應用程式

接下來,我們將建立一個簡單的客戶端應用程式,用於與我們的RAD Server ToDo列表資源互動。

  1. 在Delphi中建立一個新的多裝置應用程式專案。
  2. 新增按鈕和編輯框,用於與使用者互動。
  3. 使用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中的元件,如TRESTClientTRESTRequestTRESTResponse,來傳送HTTP請求和處理回應。