2011-07-16 16 views
7

DataSnapを使用してJSONP(JSDA with Padding)ソリューションを実装する方法はありませんが、誰かがこの問題を解決した場合に備えて、ここでこの質問を投げたいと思います。Delphi DataSnap RESTサーバーでJSONPを使用する方法はありますか?

背景JSONPは、HTMLスクリプト要素のクロスサイト参照機能を利用して、XmlHttpRequestクラスの同じ起点ポリシーを克服するメカニズムです。 XmlHttpRequestを使用すると、HTMLドキュメントを提供したのと同じドメインからのみデータ(JSONオブジェクト)を取得できます。しかし、複数のサイトからデータを取得し、そのデータをブラウザのコントロールにバインドする場合はどうしたらよいでしょうか?

JSONPでは、script要素のsrc属性はJavaScriptファイルを参照せず、代わりにWebメソッド(HTMLが取得された別のドメインに存在する可能性がある)を参照します。このWebメソッドは、JavaScriptを返します。

スクリプトタグは、返されたデータがJavaScriptファイルであるとみなし、通常どおりに実行します。しかし、Webメソッドが実際に返すものは、リテラルJSONオブジェクトをパラメータとする関数呼び出しです。呼び出される関数が定義されていると仮定すると、この関数はJSONオブジェクトを実行して操作することができます。たとえば、この関数はJSONオブジェクトからデータを抽出し、そのデータを現在のドキュメントにバインドできます。

JSONPの長所と短所は広範に議論されています(これは非常に深刻なセキュリティ上の問題です)。ここではこれを繰り返す必要はありません。

DelphiのDataSnap RESTサーバーでJSONPを使用する方法を知りたがっている人がいます。私はそれを見ているように、ここに問題があります。

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

函Webメソッドは、次のような呼び出し何かを返します:

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

とworkit機能のようなものに見えるかもしれません典型的なJSONPの使い方は次のようになりますscriptタグを含むことができこの:

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のではありません。

アイデア?

+2

質問は "に減少させることができますDataSnapは、クライアントに送信される前に生成されたJSON応答を変更できるフィルタ/フック/イベントを提供していますか? – mjn

答えて

9

が正確にJSONをカスタムJSONの処理をバイパスし、返すためのDelphiのDataSnap REST方式での方法があります欲しいです。 http://blogs.embarcadero.com/mathewd/2011/01/18/invocation-metadata/

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

情報:ここで私はjqGridにプレーンなデータを返すために(私のリラックス枠組みの中で)使用するクラスの関数です。

ところで、REST関数の結果にnilを割り当てることができます。

+0

ありがとう、マルコ!素晴らしい解決策。このテクニックについてのMat DeLongのブログへのリンクを含めてくれてありがとう。 –

+0

はい、私のブログMarcoにリンクしてくれてありがとう:) –

+0

リンク 'http:// blogs.embarcadero.com/mathewd/2011/01/18/invocation-metadata /'にアクセスできなくなりました。 'TRlxjqGrid'クラスについて教えてください。 –

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; 
+0

非常に良い解決策。ご返信ありがとうございます。私はあなたの答えとマルコの答えの両方を正しいものとして受け入れることができればいいと思う。私はその単純さのためにマルコの答えを受け入れました。しかし、私はあなたの解決策に投票しました。 –

+0

@Caryありがとう!マルコの答えも好きですが、 'TDSInvocationMetadata'について学ぶことは非常に面白いです。残念ながら、リクエストに関する情報は公開していないようです。場合によっては、サーバーメソッド内から要求データにアクセスすることが有効な場合もあります。 –

+0

非常に良い解決策ですが、私のコードでDoCommandプロシージャは呼び出されません。 Delphi XE7を使用.. –

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; 
+0

私はこのアプローチを試みる機会がありませんでしたが、貢献していただきありがとうございます。 –

+0

これは良い解決策です。 –

2

答えをNicolás Loaizaからトレース・イベントに顧客のレスポンスヘッダを設定し、TDSHTTPServiceのための解決策を見つけるために私を動機:

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; 
関連する問題