jsonrpcclient.pp 4.8 KB

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