簡體   English   中英

有沒有辦法將 JSONP 與 Delphi DataSnap REST 服務器一起使用?

[英]Is there a way to use JSONP with a Delphi DataSnap REST server?

似乎沒有辦法使用 DataSnap 實現JSONP (帶填充的 JSON)解決方案,但我想在這里提出這個問題,以防有人解決了這個問題。

背景:JSONP 是一種利用 HTML 腳本元素的跨站點引用功能來克服 XmlHttpRequest class 的同源策略的機制。 使用 XmlHttpRequest 您只能從為 HTML 文檔提供服務的同一域中獲取數據(JSON 對象)。 但是,如果您想從多個站點檢索數據並將該數據綁定到瀏覽器中的控件,該怎么辦?

使用 JSONP,腳本元素的 src 屬性不會引用 JavaScript 文件,而是引用 Web 方法(可以駐留在檢索 Z4C4AD5FCA2E7A3F74DBB1CED0038AA 的不同域中的方法)。 此 Web 方法返回 JavaScript。

script標簽假設返回的數據是JavaScript文件,正常執行。 然而,Web 方法實際返回的是一個 function 調用,它的參數為 JSON ZA8CFDE639114B6666。 假設定義了被調用的function,則function執行並可以對Z0ECD11C1D7A287401D148A23BBD7A2F456EB2666693ACBDBDBD11C1D7A287401D148A23BBD7A2F496EB2696進行操作。 例如,function 可以從 JSON object 中提取數據並將該數據綁定到當前文檔。

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});

工作台 function 可能看起來像這樣:

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。

有任何想法嗎?

在 Delphi DataSnap REST 方法中有一種方法可以繞過自定義 JSON 處理並准確返回 Z0ECD11C1D7A2BBD78740。 這是一個 class function 我使用(在我的 Relax 框架中)將純數據返回到 jqGrid:

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

http://blogs.embarcadero.com/mathewd/2011/01/18/invocation-metadata/上的信息。
https://mathewdelong.wordpress.com/2011/01/18/invocation-metadata/上的信息。

順便說一句,您可以將 nil 分配給 REST function 的結果。

您可以編寫一個 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;

Nicolás Loaiza的回答激勵我尋找 TDSHTTPService 的解決方案,在 Trace 事件中設置客戶響應 header:

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;

在 DataSnap 中可以很容易地解決源策略問題。 您可以通過這種方式自定義響應 header:

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;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM