Simple XML-RPC Server lazarus / 38-Networking and Web Programming ---------------------------------------------------------------------- Edson · 2015-09-28 04:40 Is there a simple library/code to implement a simple XML-RPC server? I was trying to use "Web Service Toolkit" and after reading the wiki for hours, I'm still confused. It's too complicated, just to public a simple function like hello_world. ---------------------------------------------------------------------- derek.john.evans · 2015-09-28 06:07 Unsure. You could use the units fphttpclient and DOM to transfer your own XML. I guess, the complexity comes with the automated binding. Ok, here comes another "opinion". I hate automated remoting. To me it feels a little brittle and over cooked. I haven't dont much networking stuff in years, but here is how I did it in C#. Write a HTTP server. Use the HTTP querystring for parameters. (I reparse the querystring, because I dont need the name/values). The sample code remotes 7 functions. Its pretty easy to return arrays. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program server; {$mode objfpc}{$H+} uses  FileUtil,  fphttpserver,  HttpDefs,  Math,  SysUtils,  UriParser,  Variants; type  TMyRemotingServer = class(TFPHttpServer)    procedure HandleRequest(var ARequest: TFPHTTPConnectionRequest; var AResponse: TFPHTTPConnectionResponse);    override;  end;   procedure TMyRemotingServer.HandleRequest(var ARequest: TFPHTTPConnectionRequest;  var AResponse: TFPHTTPConnectionResponse);     function GetString(const AIndex: Integer): String;    begin      Assert(InRange(AIndex, 0, ARequest.QueryFields.Count - 1));      Result := HTTPDecode(ARequest.QueryFields[AIndex]);    end;     function GetInteger(const AIndex: Integer): Integer;    begin      Result := StrToInt(GetString(AIndex));    end;     function GetDouble(const AIndex: Integer): Double;    begin      Result := StrToFloat(GetString(AIndex));    end;     procedure AddString(const AValue: String);    begin      AResponse.Contents.Add(HTTPEncode(AValue));    end;     procedure AddDouble(const AValue: Double);    begin      AddString(FloatToStr(AValue));    end;   begin    try      AResponse.Code := 200;      ARequest.QueryFields.StrictDelimiter := True;      ARequest.QueryFields.Delimiter := '&';      ARequest.QueryFields.DelimitedText := ARequest.QueryString;      case LowerCase(ParseURI(ARequest.URI).Document) of        'hello': begin        AddString('Hello ' + GetString(0) + '.');        end;        'copy': begin          AddString(Copy(GetString(0), GetInteger(1), GetInteger(2)));        end;        'readfiletostring': begin        AddString(ReadFileToString(GetString(0)));        end;        'uppercase': begin          AddString(UpperCase(GetString(0)));      end;        'lowercase': begin          AddString(LowerCase(GetString(0)));        end;        'sin': begin        AddDouble(Sin(GetDouble(0)));        end;        'cos': begin        AddDouble(Cos(GetDouble(0)));        end else begin          raise Exception.Create('Unknown Command');        end;      end;    except      on E: Exception do begin        AResponse.CodeText := E.Message;        AResponse.Code := 400;      end;    end;  end; begin  try    with TMyRemotingServer.Create(nil) do begin      try        Port := 1234;        Active := True;      finally        Free;      end;    end;  except    on E: Exception do WriteLn(E.Message);  end;  ReadLn;end.  Then on the client side, I code up a generic Http remote function which accepts an array of string, and returns an array of string. Using that function, I create local function wrappers for each of the remote functions. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program client; {$mode objfpc}{$H+} uses  Classes,  fphttpclient,  HttpDefs,  SysUtils,  Types,  Variants;   function HttpRemoteCall(const AName: String; const AParams: array of String;  const AExpectedCount: Integer): TStringDynArray;  var    LIndex: Integer;  begin    with TStringList.Create do begin      try        for LIndex := Low(AParams) to High(AParams) do begin          Add(HttpEncode(AParams[LIndex]));      end;        StrictDelimiter := True;        Delimiter := '&';      DelimitedText := TFPHTTPClient.SimpleGet('http://127.0.0.1:1234/' + AName + '?' + DelimitedText);        Assert(Count = AExpectedCount);      SetLength(Result, Count);        for LIndex := 0 to Count - 1 do begin          Result[LIndex] := HttpDecode(Strings[LIndex]);        end;      finally        Free;      end;    end;  end;   function HttpSin(const AValue: Double): Double;  begin    Result := StrToFloat(HttpRemoteCall('sin', [FloatToStr(AValue)], 1)[0]);  end;   function HttpCos(const AValue: Double): Double;  begin    Result := StrToFloat(HttpRemoteCall('cos', [FloatToStr(AValue)], 1)[0]);  end;   function HttpReadFileToString(const AFileName: String): String;  begin    Result := HttpRemoteCall('readfiletostring', [AFileName], 1)[0];  end;   function HttpHello(const AString: String): String;  begin    Result := HttpRemoteCall('hello', [AString], 1)[0];  end;   function HttpCopy(const AString: String; const APos, ALen: Integer): String;  begin    Result := HttpRemoteCall('copy', [AString, IntToStr(APos), IntToStr(ALen)], 1)[0];  end; begin  try    WriteLn(HttpReadFileToString('C:\Documents and Settings\Admin\Desktop\Remoting\server.lpr'));    WriteLn(HttpHello('World'));    WriteLn('Sin(1.0)=', HttpSin(1.0));  WriteLn('Cos(0.5)=', HttpCos(0.5));    WriteLn('Copy=', HttpCopy('Lazarus is Fantasic!', 1, 7));  except    on E: Exception do WriteLn(E.Message);  end;  ReadLn;end.  I got pretty good results using this method. Note: Obviously, the ideal situation would be to have all your remoted API in a unit, and written in such a way that the local "client" code doesn't even look like it is using a remoted API. ---------------------------------------------------------------------- Edson · 2015-09-28 17:52 Good. It's a simple way of doing a custom Web Service, using the GET style.  Sadly, I need to implement a real XML-RPC server, to listen some VB clients. I don't need a WSDL file, just to response to 5 or 6 functions with simple parameters. I really miss the simplicity of Python doing this: Code: [Select]from SimpleXMLRPCServer import SimpleXMLRPCServer def hello_world(nombre):     return "hello" server = SimpleXMLRPCServer(("localhost", 8080)) server.register_function(hello_world) server.serve_forever() ---------------------------------------------------------------------- Edson · 2015-09-29 17:14 Finally, I had to implement my own XML-RPC Server.  I share this for someone who need a simpe Server: https://github.com/t-edson/ThXmlRpc With this unit, a Server can be implemented as simple as: Code: [Select]program server; {$mode objfpc}{$H+} uses   SysUtils, ThXmlRpc; procedure Hello(params: TthXmlRpcParams; response: TthXmlRpcResult); begin   if (params.Count<>1) or (params[0].rpcTyp<>xmlRpcString) then begin     writeln('Parameters error.');     exit;   end;   response.valStr := 'Hi ' + params[0].valStr + '!'; end; var   Serv: TthXmlRpcServer; begin   RegisterFunction('Hello', @Hello);   writeln('Listening...');   Serv := TthXmlRpcServer.Create(nil);   try     Serv.Port:=1234;     Serv.Active:=true;   finally     Serv.Free;   end;   ReadLn; end. ---------------------------------------------------------------------- tigerA15 · 2015-11-30 06:20 marked