5

多数のサービスとメソッドを公開するRemObjectsSDKHTTPサーバーがあります。パラメータをSOAP/JSONとして渡すのではなく、URIを介してメソッドを呼び出すことは可能ですか?

http://www.mywebservice.com/servicename/methodname?param1=xxx&param2=yyy
4

3 に答える 3

3

これは、見栄えがよく、JSONを返すnorgepaulのソリューションの遊びです。これは、の子孫を使用してHTTPリクエストをインターセプトするという同じ考え方に基づいていますTROIndyHTTPServerが、今回はリクエストのパラメータを修正するだけでなく、クライアントが送信しなかった「JSON」投稿を作成しています。

デフォルトの「VCLStandalon」サーバー実装でテストするために使用したコードは次のとおりです。

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;

この種のコードを配置すると、次のようなリクエストを行うことができます。

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

戻ります(ブラウザで!)

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

この:

 http://localhost:8080/json/getservertime

これを返します(まあ、この記事の執筆時点で):

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

結果(ブラウザまたは外部アプリケーション)は、ROのコードを使用して「JSONメッセージ」としてフォーマットされているため、純粋なJSONです。

于 2013-02-01T17:25:29.520 に答える
3

アップデート

サーバーの子孫の改良版を書きました。これは、フォーマットされた URI を JSON オブジェクトに変換します。このオブジェクトは、後で RO JSON メッセージ ハンドラーによって処理されます。

デフォルトの処理方法は、URI を無視することです。

次のような URI を受け入れるように変更URIHandlingMethodします。urhJSON

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

次のような URI を受け入れるように設定URIHandlingMethodします。urhParametersto

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

コードは次のとおりです。

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.

元の回答

これは、アンドレの回答のフォローアップです。

RemObjects SDK の現在のバージョンでは、次の URI が機能するはずですが、機能しません。

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

2 つの理由があります。

  • URI は、メッセージ ハンドラーに渡される前にデコードされません。文字のいずれかがエンコードされている場合 (%20 など)、JSON エラーが発生します。
  • ROIndyHTTPServer コードに、URI パラメータを誤って処理するバグがあるようです。

両方の問題を修正する ROIndyHTTPServer の子孫を作成しました。コードは次のとおりです。

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.

これは私の質問には答えませんが、同様の問題を抱えている人のための回避策です。

RO SDK がカスタム URI の使用をサポートしているかどうかを知りたいです。

于 2013-02-01T14:20:29.220 に答える
2

私の知る限り:いいえ。

JSON 構造体を使用したある種の REST 呼び出しのみを行うことができます: http://www.mywebservice.com/JSON ?{"id":"{392543cf-f110-4ba3-95471b02ce5bd693}","method":"servicename.メソッド名","params":{"param1":"xxx","param2":"yyy"}}:

ところで:DataAbstract(ROに基づく)にはRESTインターフェースがありますが、RO自体にはありません... :(

于 2013-02-01T11:51:29.733 に答える