jsonrpcclient.pp 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. {
  2. This file is part of the Free Component Library
  3. Demonstrate bare-bones client-side JSON-RPC functionality using Invoke.
  4. Copyright (c) 2022 by Michael Van Canneyt [email protected]
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. program jsonrpcclient;
  12. {$mode objfpc}{$H+}
  13. {$if not defined(CPU386) and not defined(WIN64)}
  14. {$define useffi}
  15. {$endif}
  16. uses
  17. SysUtils, Classes, fpjson, jsonparser, jsonscanner, fphttpclient,
  18. rtti, typinfo {$ifdef useffi}, ffi.manager{$endif}, myapi, fpjsonvalue;
  19. type
  20. TJsonRpcClient = class(TVirtualInterface)
  21. private
  22. class var
  23. aID : Integer;
  24. private
  25. fName: String;
  26. fBaseUrl: String;
  27. procedure HandleInvoke(aMethod: TRttiMethod; const aArgs: TValueArray; out
  28. aResult: TValue);
  29. function DoRequest(aRequest: TJSONData): TJSONData;
  30. public
  31. constructor Create(aTypeInfo: PTypeInfo; const aBaseUrl: String);
  32. generic class function GetClientIntf<T: IInterface>(const aBaseUrl: String): T;
  33. end;
  34. procedure TJsonRpcClient.HandleInvoke(aMethod: TRttiMethod;
  35. const aArgs: TValueArray; out aResult: TValue);
  36. var
  37. request, response: TJSONObject;
  38. args: specialize TArray<TRttiParameter>;
  39. arg: TRttiParameter;
  40. varParamCount, argidx, i: LongInt;
  41. resobj,argobj: TJSONObject;
  42. value: TValue;
  43. begin
  44. VarParamCount:=0;
  45. request := TJSONObject.Create;
  46. try
  47. request.Add('method', aMethod.Name);
  48. request.Add('class', fName);
  49. request.Add('jsonrpc','2.0');
  50. inc(aID);
  51. request.Add('id',aID);
  52. { skip Self argument }
  53. argidx := 1;
  54. argobj := TJSONObject.Create;
  55. args := aMethod.GetParameters;
  56. for i := 0 to High(args) do begin
  57. arg := args[i];
  58. if [pfHidden,pfSelf] * arg.Flags <> [] then
  59. Continue
  60. else if ([pfVar,pfOut] * arg.Flags)<>[] then
  61. Inc(VarParamCount);
  62. argobj.Add(arg.Name, ValueToJSON(aArgs[argidx], arg.ParamType));
  63. Inc(argidx);
  64. end;
  65. request.Add('params', argobj);
  66. aResult:=Default(TValue);
  67. response := DoRequest(request) as TJSONObject;
  68. try
  69. if (VarParamCount=0) then
  70. begin
  71. if Assigned(aMethod.ReturnType) then
  72. aResult := JSONToValue(response.Elements['result'], aMethod.ReturnType);
  73. end
  74. else
  75. begin
  76. resObj:=response.Objects['result'];
  77. if Assigned(aMethod.ReturnType) then
  78. aResult := JSONToValue(resObj.Elements['$result'], aMethod.ReturnType);
  79. argidx := 1;
  80. for i := 0 to High(args) do
  81. begin
  82. arg := args[i];
  83. if pfHidden in arg.Flags then
  84. Continue;
  85. if arg.Flags * [pfOut, pfVar] = [] then
  86. begin
  87. Inc(argidx);
  88. Continue;
  89. end;
  90. value := JSONToValue(resObj.Elements[arg.Name], arg.ParamType);
  91. value.ExtractRawData(aArgs[argidx].GetReferenceToRawData);
  92. Inc(argidx);
  93. end;
  94. end;
  95. finally
  96. response.Free;
  97. end;
  98. finally
  99. request.Free;
  100. end;
  101. end;
  102. function TJsonRpcClient.DoRequest(aRequest: TJSONData): TJSONData;
  103. var
  104. client: TFPHTTPClient;
  105. ss: TStringStream;
  106. parser: TJSONParser;
  107. resp: String;
  108. begin
  109. ss := TStringStream.Create(aRequest.AsJSON);
  110. try
  111. client := TFPHTTPClient.Create(Nil);
  112. try
  113. client.RequestBody := ss;
  114. resp := client.Post(fBaseUrl + fName);
  115. Writeln('Got response:');
  116. Writeln(resp);
  117. //parser := TJSONParser.Create(client.Post(fBaseUrl + fName), [joUTF8]);
  118. parser := TJSONParser.Create(resp, [joUTF8]);
  119. try
  120. Result := parser.Parse;
  121. finally
  122. parser.Free;
  123. end;
  124. finally
  125. client.Free;
  126. end;
  127. finally
  128. ss.Free;
  129. end;
  130. end;
  131. constructor TJsonRpcClient.Create(aTypeInfo: PTypeInfo; const aBaseUrl: String);
  132. begin
  133. inherited Create(aTypeInfo, @HandleInvoke);
  134. fBaseUrl := aBaseUrl;
  135. if fBaseUrl[Length(fBaseUrl)] <> '/' then
  136. fBaseUrl := fBaseUrl + '/';
  137. fName := aTypeInfo^.Name;
  138. end;
  139. generic class function TJsonRpcClient.GetClientIntf<T>(const aBaseUrl: String): T;
  140. var
  141. client: TJsonRpcClient;
  142. td: PTypeData;
  143. begin
  144. client := TJsonRpcClient.Create(PTypeInfo(TypeInfo(T)), aBaseUrl);
  145. td := GetTypeData(PTypeInfo(TypeInfo(T)));
  146. client.QueryInterface(td^.GUID, Result);
  147. end;
  148. var
  149. client: IMyInterface;
  150. arr: TStringArray;
  151. s: String;
  152. res: Boolean;
  153. begin
  154. client := TJsonRpcClient.specialize GetClientIntf<IMyInterface>('http://127.0.0.1:8080/RPC/');
  155. try
  156. Writeln('===== Testing SayHello');
  157. client.SayHello;
  158. Writeln('===== Testing DoSum');
  159. Writeln(client.DoSum(2, 6));
  160. Writeln('===== Testing Split');
  161. arr := client.Split('Hello FPC World', ' ');
  162. Writeln('Split data:');
  163. for s in arr do
  164. Writeln(#9, s);
  165. Writeln('===== Testing DoVarTest');
  166. s := 'Foobar';
  167. res := client.DoVarTest(s);
  168. Writeln(res, ' ', s);
  169. s := 'Test';
  170. res := client.DoVarTest(s);
  171. Writeln(res, ' ', s);
  172. // Writeln('===== Testing Echo');
  173. // writeln(Client.Echo(['This','is','Sparta']));
  174. finally
  175. client := Nil;
  176. end;
  177. {$ifndef unix}
  178. Readln;
  179. {$endif}
  180. end.