zoukankan      html  css  js  c++  java
  • Can RemObjects SDK parameters be passed via the URI?

    http://stackoverflow.com/questions/14645022/can-remobjects-sdk-parameters-be-passed-via-the-uri/14648334#14648334

    I've written an improved version of the server descendant. This one converts a formatted URI into a JSON object that will subsequently be handled by the RO JSON Message handler.

    The default handing method is to ignore the URI.

    Change URIHandlingMethod to urhJSON to accept a URI like this:

    http://www.mywebservice.com/json?{JSON OBJECT}

    Set URIHandlingMethod to urhParametersto to accept a URI like this:

    http://www.mywebservice.com/json/service/method?param1=xxx&param2=yyy

    Here's the code:

    unit ROJSONURIIndyHTTPServer ;
    
    interface
    
    uses
      SysUtils, Classes,
    
      uROIndyHTTPServer,
    
      IdURI, IdCustomHTTPServer;
    
    type
      TURIHandlingMethod = (
        urhNone,
        urhJSON,
        urhParameters
      );
    
      TROJSONURIIndyHTTPServer = class(TROIndyHTTPServer)
      private
        FURIHandlingMethod: TURIHandlingMethod;
        FJSONVersion: String;
    
        function ConvertURIToJSON(const Document, Params: String): String;
        function NextBlock(var Value: String; Delimiter: Char = '/'): String;
      protected
        procedure InternalServerCommandGet(AThread: TIdThreadClass; RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo); override;
      public
        constructor Create(AOwner: TComponent); override;
      published
        property URIHandlingMethod: TURIHandlingMethod read FURIHandlingMethod write FURIHandlingMethod;
        property JSONVersion: String read FJSONVersion write FJSONVersion;
      end;
    
    implementation
    
    { TROJSONURIIndyHTTPServer }
    
    constructor TROJSONURIIndyHTTPServer.Create(AOwner: TComponent);
    begin
      inherited;
    
      FJSONVersion := '1.1';
    end;
    
    function TROJSONURIIndyHTTPServer.NextBlock(var Value: String; Delimiter: Char): String;
    var
      p: Integer;
    begin
      p := 1;
    
      while (p <= length(Value)) and (Value[p] <> Delimiter) do
        Inc(p);
    
      if p = length(Value) then
        Result := Value
      else
        Result := copy(Value, 1, p - 1);
    
      Value := copy(Value, p + 1, MaxInt);
    end;
    
    function TROJSONURIIndyHTTPServer.ConvertURIToJSON(const Document, Params: String): String;
    const
      JSONObjectTemplate = '{"method":"%s.%s"%s,"version": "%s"}';
      JSONParamTemplate = '"%s":"%s"';
      JSONParamsTemplate = ',"params":{%s}';
    var
      CallService, CallMessage,
      ParsedDocument, ParsedParams, JSONParams,
      Param, ParamName, ParamValue: String;
      i: Integer;
    begin
      Result := '';
    
      ParsedDocument := Trim(Document);
    
      // Remove the leading /
      if (length(Document) > 0) and
         (Document[1] = '/') then
        NextBlock(ParsedDocument);
    
      // Remove the message type
      NextBlock(ParsedDocument);
    
      // Extract the service
      CallService := NextBlock(ParsedDocument);
    
      // Exctract the service message (method)
      CallMessage := NextBlock(ParsedDocument);
    
      JSONParams := '';
      ParsedParams := Params;
    
      while ParsedParams <> '' do
      begin
        // Extract the parameter and value
        Param := NextBlock(ParsedParams, '&');
    
        // See RFC 1866 section 8.2.1. TP
        Param := StringReplace(Param, '+', ' ', [rfReplaceAll]);  {do not localize}
    
        // Extract the parameter name
        ParamName := NextBlock(Param, '=');
    
        // Extract the parameter value
        ParamValue := Param;
    
        // Add a delimiter if required
        if JSONParams <> '' then
          JSONParams := JSONParams + ',';
    
        // Build the JSON style parameter
        JSONParams := JSONParams + format(JSONParamTemplate, [ParamName, ParamValue]);
      end;
    
      if JSONParams <> '' then
        JSONParams := format(JSONParamsTemplate, [JSONParams]);
    
      // Make sure we have values for all the object variables, then build the JSON object
      if (CallService <> '') and
         (CallMessage <> '') and
         (FJSONVersion <> '') then
        Result := format(JSONObjectTemplate, [CallService, CallMessage, JSONParams, JSONVersion]);
    end;
    
    procedure TROJSONURIIndyHTTPServer.InternalServerCommandGet(
      AThread: TIdThreadClass; RequestInfo: TIdHTTPRequestInfo;
      ResponseInfo: TIdHTTPResponseInfo);
    begin
      if FURIHandlingMethod in [urhJSON, urhParameters] then
      begin
        // Parse parameters into JSON if required
        if FURIHandlingMethod = urhParameters then
          RequestInfo.UnparsedParams := ConvertURIToJSON(RequestInfo.Document, RequestInfo.UnparsedParams);
    
        // Decode the URI e.g. converts %20 to whitespace
        RequestInfo.UnparsedParams := TIdURI.URLDecode(RequestInfo.UnparsedParams);
    
        //  This works around a bug in TROIndyHTTPServer. By adding a whitespace to the
        //  end of the QueryParams it forces the http server to process the parameters
        RequestInfo.QueryParams := TIdURI.URLDecode(RequestInfo.QueryParams) + ' ';
      end;
    
      inherited;
    end;
    
    end.

    Original Answer

    This is a follow up to André's answer.

    With the current version of RemObjects SDK the following URI should work, but doesn't:

    http://www.mywebservice.com/JSON?{"id":"{392543cf-f110-4ba3-95471b02ce5bd693}","method":"servicename.methodname","params":{"param1":"xxx","param2":"yyy"}}:

    There are 2 reasons why:

    • The URI is not decoded before it is passed to the message handler. This results in a JSON error if any of the characters have been encoded e.g. %20 etc.
    • There seems to be a bug in the ROIndyHTTPServer code that mishandles the URI parameters.

    I've created a ROIndyHTTPServer descendant that fixes both problems. Here's the code:

    unit FixedROIndyHTTPServer;
    
    interface
    
    uses
      SysUtils, Classes,
    
      uROIndyHTTPServer,
    
      IdURI, IdCustomHTTPServer;
    
    type
      TFixedROIndyHTTPServer = class(TROIndyHTTPServer)
      protected
        procedure InternalServerCommandGet(AThread: TIdThreadClass; RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo); override;
      public
        constructor Create(AOwner: TComponent); override;
      end;
    
    implementation
    
    { TFixedROIndyHTTPServer }
    
    constructor TFixedROIndyHTTPServer.Create(AOwner: TComponent);
    begin
      inherited;
    end;
    
    procedure TFixedROIndyHTTPServer.InternalServerCommandGet(
      AThread: TIdThreadClass; RequestInfo: TIdHTTPRequestInfo;
      ResponseInfo: TIdHTTPResponseInfo);
    begin
      // This fixes 2 issues with TROIndyHTTPServer
      //  1) It decodes the parameters e.g. converts %20 to whitespace
      //  2) It adds a whitespace to the end of the QueryParams. This
      //     forces the http server to process the parameters.
    
      RequestInfo.QueryParams := TIdURI.URLDecode(RequestInfo.QueryParams) + ' ';
      RequestInfo.UnparsedParams := TIdURI.URLDecode(RequestInfo.UnparsedParams);
    
      inherited;
    end;
    
    end.

    This doesn't answer my question, but it is a workaround for anybody having similar problems.

    I'm still keen to hear if RO SDK supports the use of custom URIs.

    Here's a play on norgepaul's solution that looks good and returns JSON. It's based on the same idea of intercepting the HTTP request using a descendant of TROIndyHTTPServer, but this time I'm not only fixing the parameters of the request, I'm creating the "JSON" post that the client didn't send!

    Here's the code that I used to test with the default "VCL Standalon" server implementation:

    TUriROIndyHTTPServer = class(TROIndyHTTPServer)
    protected
      procedure InternalServerCommandGet(AThread: TIdThreadClass; RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo); override;
    end;
    
    procedure TUriROIndyHTTPServer.InternalServerCommandGet(AThread: TIdThreadClass;RequestInfo: TIdHTTPRequestInfo; ResponseInfo: TIdHTTPResponseInfo);
    var A, B: Integer;
        NewPost: AnsiString;
    begin
      if RequestInfo.Document = '/json/sum' then
        begin
          // Extract the parameters
          A := StrToIntDef(RequestInfo.Params.Values['a'], 0);
          B := StrToIntDef(RequestInfo.Params.Values['b'], 0);
          NewPost := AnsiString(Format('{"version":"1.1","method":"NewService.Sum","params":{"A":"%d","B":"%d"}}', [A, B]));
    
          // Prepare the (fake) post-stream
          RequestInfo.PostStream.Free;
          RequestInfo.PostStream := TMemoryStream.Create;
          RequestInfo.PostStream.Write(NewPost[1], Length(NewPost));
          RequestInfo.PostStream.Position := 0;
        end
      else if RequestInfo.Document = '/json/getservertime' then
        begin
          // Extract the parameters
          NewPost := '{"version":"1.1","method":"NewService.GetServerTime"}';
    
          // Prepare the (fake) post-stream
          RequestInfo.PostStream.Free;
          RequestInfo.PostStream := TMemoryStream.Create;
          RequestInfo.PostStream.Write(NewPost[1], Length(NewPost));
          RequestInfo.PostStream.Position := 0;
        end;
    
      inherited;
    end;

    With this sort of code in place, I can make requests like this:

    http://localhost:8080/json/sum?a=1&b=2

    returns (in browser!)

     {"version":"1.1","result":"3"}

    and this:

     http://localhost:8080/json/getservertime

    returns this (well, at the time of this writing):

    {"version":"1.1","result":"2013-02-01T19:24:24.827"}

    The result (in browser or foreign application) is pure JSON because it's been formated as a "JSON Message" using RO's code.

  • 相关阅读:
    指针
    初级程序员面试不靠谱指南(七)
    初级程序员面试不靠谱指南(六)
    Hadoop 中利用 mapreduce 读写 mysql 数据
    Mapreduce的文件和hbase共同输入
    mapreduce多文件输出的两方法
    mapreduce中一个map多个输入路径
    GDB介绍
    超强的指针学习笔记
    iOS开发之Appstore篇——版本更新
  • 原文地址:https://www.cnblogs.com/xiefang2008/p/6401353.html
Copyright © 2011-2022 走看看