How to Use mORMot to Build Rest Services and Consume Them

如何使用mORMot构建Rest服务并使用

服务端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
var
  FModel: TSQLModel;
  FDB: TSQLRestServer;
  FServer: TSQLHttpServer;

    //Create Server
    FModel := TSQLModel.Create([], 'api');
    FDB := TSQLRestServerFullMemory.Create(FModel, 'test.json', false, false);
    //reg services
    FDB.ServiceRegister(TTestService, [TypeInfo(ITestService)], sicShared);

    //setting and start it
    FServer := TSQLHttpServer.Create(httpPort, [FDB], '+',
    useHttpApiRegisteringURI);
    FServer.AccessControlAllowOrigin := '*'; // allow cross-site AJAX queries

接口和DTO单元

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type
  TTest = record
    A, B: string;
  end;

  TTest1 = class(TPersistent)
  private
    fB: string;
    fA: string;
  published
    property A: string read fA write fA;
    property B: string read fB write fB;
  end;

  ITestService = interface(IInvokable)
    ['{85B34854-522A-4218-A66C-4E2C274BD318}']
    function TestMethod(const D: TTest): TServiceCustomAnswer;
    procedure Fuck(const D: TTest; out r: TTest1);
  end;

//初始化的时候注册一下
initialization
  TInterfaceFactory.RegisterInterfaces([TypeInfo(IClockService)]);

服务类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  TTestService = class(TInterfacedObject, ITestService)
  public
  published
    function TestMethod(const D: TTest): TServiceCustomAnswer;
    procedure Fuck(const D: TTest; out r: TTest1);
  end;

implementation

function TTestService.TestMethod(const D: TTest): TServiceCustomAnswer;
begin
  Result.Status := 200;

  Result.Content := '{"A":"sdsdsd","B":"sdsfdgfsweeed"}';
end;

procedure TTestService.Fuck(const D: TTest; out r: TTest1);
begin

  r.A := 'Hellsinsidn';
  r.B := 'sdwfgfgf';
end;

{ TTest1 }

end.

正常实现接口方法并编写逻辑处理代码即可, 返回值为 TServiceCustomAnswer 的话就可以返回自定义的JSON内容给客户端

客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//定义几个常量
const
  ROOM_NAME = 'api';
  SERVICE_503 = 'HTTP服务不可用';
type
  // 配置一下服务器
  TServiceConfig = record
    Host:string;
    Port:string;
    /// <summary>
    ///   载入配置
    /// </summary>
    procedure LoadConf;
  end;  
//小小地封装一下, 方便调用
TSOAClient = class(TPersistentWithCustomCreate)
  private
    Client: TSQLHttpClient;
    Model: TSQLModel;
    Config: TServiceConfig;
    /// <summary>
    ///   注册服务接口
    /// </summary>
    procedure RegServices;
  public
    /// <summary>
    ///   创建SOA客户端实例
    /// </summary>
    /// <param name="pvConfig">
    ///   配置
    /// </param>
    constructor Create(pvConfig: TServiceConfig);
    destructor Destory; overload;

    /// <summary>
    ///   取得接口实例
    /// </summary>
    /// <param name="I">
    ///   接口
    /// </param>
    /// <param name="obj">
    ///   返回接口实例
    /// </param>
    /// <returns>
    ///   是否成功
    /// </returns>
    function Get(I: TGUID; out obj): Boolean;
  end;

implementation

function TSOAClient.Get(I: TGUID; out obj): Boolean;
begin
  Result := Client.Services.Resolve(I, obj);
end;

destructor TSOAClient.Destory;
begin
  FreeAndNil(Client);
  FreeAndNil(Model);
end;

procedure TSOAClient.RegServices;
begin
  //  Client.ServiceRegister([TypeInfo(ITestService)],sicShared);
  Client.ServiceDefine([ITestService
  //新接口注册在这里


  ], sicShared);
end;

constructor TSOAClient.Create(pvConfig: TServiceConfig);
begin
  Config := pvConfig;
  Model := TSQLModel.Create([], ROOM_NAME);
  Client := TSQLHttpClient.Create(Config.Host, Config.Port, Model);
  RegServices;
end;  

调用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var
  c:TSOAClient;
  conf:TServiceConfig;
  I:ITestService;
begin
  Result := False;
  conf.LoadConf;

  try
    c := TSOAClient.Create(conf); //创建连接客户端 连接到HTTP服务
  except
    on E: Exception do
    begin
      // SERVICE_503
      Exit;
    end;
  end;
  try
    if c.Get(ITestService,I) then
    begin
      //这里进行随意的调用即可
      //I.Fuck
    end;

  finally
    FreeAndNil(c);
  end;
end;

如果返回 TServiceCustomAnswer, 那就用正常的HTTP组件去调用, 然后自行处理返回的body即可

小结: 这种模式我觉得很适合把Delphi的DataSnap改造成这种形式, 一方面可以享受到http.sys的高效, 另一方面这种基于接口的编程方式, 可以完全脱离代理类的那套流程

记录平时瞎折腾遇到的各种问题, 方便查找
使用 Hugo 构建
主题 Stack 3.29.0Jimmy 设计