RemObjects SDK 参数是否可以通过 URI 传递



我们有一个RemObjects SDK HTTP服务器,它公开了许多服务和方法。是否可以通过 URI 调用方法,而不是将参数作为 SOAP/JSON 传递,例如

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

这是对norgepaul解决方案的玩法,看起来不错并返回JSON。它基于使用 TROIndyHTTPServer 的后代拦截 HTTP 请求的相同想法,但这次我不仅修复了请求的参数,还创建了客户端没有发送的"JSON"帖子!

以下是我用来测试默认"VCL Standalon"服务器实现的代码:

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"}
  • 我的服务器主表单粘贴链接的完整代码
  • 我的服务器主表单粘贴链接的 DFM

结果(在浏览器或外部应用程序中)是纯JSON,因为它已使用RO的代码格式化为"JSON消息"。

更新

我已经编写了服务器后代的改进版本。这将格式化的 URI 转换为随后将由 RO JSON 消息处理程序处理的 JSON 对象。

默认处理方法是忽略 URI。

URIHandlingMethod更改为urhJSON以接受如下所示的 URI:

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

URIHandlingMethod设置为 urhParametersto 以接受如下所示的 URI:

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"}}:

有两个原因:

  • 在将 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。

据我所知:没有。

您只能使用 JSON 结构执行某种 REST 调用:http://www.mywebservice.com/JSON?{"id":"{392543cf-f110-4ba3-95471b02ce5bd693}","method":"servicename.methodname","params":{"param1":"xxx","param2":"yyy"}}:

顺便说一句:DataAbstract(基于RO)有一个REST接口,但RO本身没有... :(

相关内容

  • 没有找到相关文章

最新更新