2013-02-01 31 views
5

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

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

您可能会从供应商那里得到一个更直接的答案,在connect.remobjects.com上,并不是说这不是一个好的地方。 –

+0

@ WarrenP-通常我会同意 - RemObjects有很棒的产品,但是他们的支持需要很长时间才能做出回应。我虽然这可能会更快发布在这里:) – norgepaul

+1

@norgepaul - 如果他们曾经回应... – RBA

回答

3

UPDATE

我已经写了一个服务器后代的改进版本,它将一个格式化的URI转换成一个JSON对象,由RO JSON消息处理程序处理。

默认的处理方法是忽略URI。

变化URIHandlingMethodurhJSON接受URI是这样的:

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

设置URIHandlingMethodurhParametersto接受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. 

原创回答

这是André的回答。

当前版本的RemObjects SDK的以下URI应该工作,但不会:

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

有2个原因:

  • 它是通过之前的URI没有被解码到消息处理程序。如果已经编码了任何字符,则会导致JSON错误。 %20等
  • 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。

+0

我认为你仍然应该将它发布到connect.remobjects.com –

+0

对于'urhParametersto';我认为这个答案应该得到接受勾号。 –

+0

+ CosminPrund - 好的,会的。这是你的帖子,给了我这个想法:) – norgepaul

2

据我所知:不是。

只能使用JSON结构做一些REST调用的: http://www.mywebservice.com/JSON { “ID”: “{392543cf-f110-4ba3-95471b02ce5bd693}”, “方法”: “servicename.methodname”,“PARAMS “:{” 参数1 “:” XXX”, “参数2”: “YYY”}}:

BTW:DataAbstract(基于RO)有一个REST接口,但RO本身不... :(

+0

这就是我现在拨打电话的方式,但我们的客户需要URI中的参数。我们有DataAbstract,但是我怀疑这会有所帮助,因为DA Rest实现可能只处理DataAbstract请求,而不是普通的SDK。我对么? – norgepaul

+0

对不起,我误解了你的答案。目前我们发布了JSON结构,我们不会将它添加到URI中。但是,我试了一下,但没有奏效。看到我的答案,确保它的工作。 – norgepaul

3

这是一个关于norgepaul's解决方案,看起来不错,并返回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"} 

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

+0

@ cosmin-尼斯的想法。这将是很好,如果它是通用的,并与任何服务/参数列表一起工作。它应该很容易实现。如果它能够工作的话,我明天就会去找它并在这里发布。 – norgepaul

+0

@norgepaul,我正在研究使其通用。我是非常新的RO-SDK,我试图弄清楚最终如何分派服务。这些信息肯定在某处。我确信我们可以使用RODL阅读器直接从RODL中提取相同的信息。解决方案绝对有可能。 –

+0

@ Cosmin - 为了保持简单我只是按照您已经在这里开始的方法。如果你有一个类似http:// webservice/json/service/method的URI?param1 = xxx&param2 = yyy只是解析出服务,方法和参数,然后创建一个JSON对象,例如{“version”:“1.1”,“method”:“service.method”,“params”:{“param1”:“xxx”,“param2”:“yyy”}} ...就是这样!如果你有时间去调查,肯定会有更优雅的方式来做到这一点:) – norgepaul