7

似乎没有办法使用 DataSnap 实现JSONP(带填充的 JSON)解决方案,但我想在这里提出这个问题,以防有人解决了这个问题。

背景:JSONP 是一种利用 HTML 脚本元素的跨站点引用能力来克服 XmlHttpRequest 类的同源策略的机制。使用 XmlHttpRequest,您只能从提供 HTML 文档的同一域中获取数据(JSON 对象)。但是,如果您想从多个站点检索数据并将该数据绑定到浏览器中的控件,该怎么办?

使用 JSONP,脚本元素的 src 属性不会引用 JavaScript 文件,而是引用 Web 方法(可以驻留在检索 HTML 的不同域中的方法)。此 Web 方法返回 JavaScript。

script 标签假定返回的数据是一个 JavaScript 文件并正常执行。然而,Web 方法实际返回的是一个以文字 JSON 对象作为参数的函数调用。假设定义了调用的函数,该函数执行并可以对 JSON 对象进行操作。例如,该函数可以从 JSON 对象中提取数据并将该数据绑定到当前文档。

JSONP 的优劣已经被广泛争论(它代表了一个非常严重的安全问题),所以这里没有必要重复。

我感兴趣的是是否有人知道如何将 JSONP 与 Delphi 的 DataSnap REST 服务器一起使用。正如我所看到的,这就是问题所在。一个典型的 JSONP 用法可能包括一个如下所示的脚本标记:

<script type="application/javascript" src="http://someserver.com/getdata?callback=workit"> </script>

getdata Web 方法将返回如下调用:

workit({"id": "Delphi Pro", "price":999});

workit 函数可能看起来像这样:

function workit(obj) {
  $("#namediv").val(obj.id);
  $("#pricediv").val(obj.price);
}

问题是 DataSnap 似乎无法返回一个简单的字符串,例如

workit({"id": "Delphi Pro", "price":999});

相反,它被包装,如下所示:

{"result":["workit({\"id\":\"Delphi Pro\",\"price\":999});"]}

显然,这不是可执行的 JavaScript。

有任何想法吗?

4

4 回答 4

9

There is a way in Delphi DataSnap REST methods to bypass the custom JSON processing and return exactly the JSON you want. Here is a class function I use (in my Relax framework) to return plain data to a jqGrid:

class procedure TRlxjqGrid.SetPlainJsonResponse(jObj: TJSONObject);
begin
  GetInvocationMetadata().ResponseCode := 200;
  GetInvocationMetadata().ResponseContent := jObj.ToString;
end;

Info at http://blogs.embarcadero.com/mathewd/2011/01/18/invocation-metadata/.
Info at https://mathewdelong.wordpress.com/2011/01/18/invocation-metadata/.

BTW, you can assign nil to the result of the REST function.

于 2011-07-18T09:47:43.230 回答
4

您可以编写一个 TDSHTTPServiceComponent 后代并将其与您的TDSHTTPService. 在以下示例中,TJsonpDispatcher在运行时创建了一个实例(以避免在 IDE 中注册它):

type
  TJsonpDispatcher = class(TDSHTTPServiceComponent)
  public
    procedure DoCommand(AContext: TDSHTTPContext; ARequestInfo: TDSHTTPRequest; AResponseInfo: TDSHTTPResponse;
      const ARequest: string; var AHandled: Boolean); override;
  end;

  TServerContainer = class(TDataModule)
    DSServer: TDSServer;
    DSHTTPService: TDSHTTPService;
    DSServerClass: TDSServerClass;
    procedure DSServerClassGetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
  protected
    JsonpDispatcher: TJsonpDispatcher;
    procedure Loaded; override;
  end;

implementation

procedure TServerContainer.DSServerClassGetClass(DSServerClass: TDSServerClass; var PersistentClass: TPersistentClass);
begin
  PersistentClass := ServerMethodsUnit.TServerMethods;
end;

procedure TServerContainer.Loaded;
begin
  inherited Loaded;
  JsonpDispatcher := TJsonpDispatcher.Create(Self);
  JsonpDispatcher.Service := DSHTTPService;
end;

procedure TJsonpDispatcher.DoCommand(AContext: TDSHTTPContext; ARequestInfo: TDSHTTPRequest;
  AResponseInfo: TDSHTTPResponse; const ARequest: string; var AHandled: Boolean);
begin
  // e.g. http://localhost:8080/getdata?callback=workit
  if SameText(ARequest, '/getdata') then
  begin
    AHandled := True;
    AResponseInfo.ContentText := Format('%s(%s);', [ARequestInfo.Params.Values['callback'], '{"id": "Delphi Pro", "price":999}']);
  end;
end;
于 2011-07-17T17:46:57.563 回答
3

在 DataSnap 中可以很容易地解决源策略问题。您可以通过这种方式自定义响应标头:

procedure TWebModule2.WebModuleBeforeDispatch(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  **Response.SetCustomHeader('access-control-allow-origin','*');**
  if FServerFunctionInvokerAction <> nil then
    FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;
于 2011-09-14T16:33:26.500 回答
3

答案Nicolás Loaiza激励我找到 TDSHTTPService 的解决方案,在 Trace 事件中设置客户响应标头:

procedure TDataModule1.DSHTTPService1Trace(Sender:
    TObject; AContext: TDSHTTPContext; ARequest: TDSHTTPRequest; AResponse:
    TDSHTTPResponse);
begin
  if AResponse is TDSHTTPResponseIndy then
    (AResponse as TDSHTTPResponseIndy).ResponseInfo.CustomHeaders.AddValue('access-control-allow-origin', '*');
end;
于 2012-06-29T03:40:55.273 回答