[英]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.