7

DataSnapを使用してJSONP (JSON with Padding)ソリューションを実装する方法はないようですが、誰かがこの問題を解決した場合に備えて、この質問をここに投げ出します。

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

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

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

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

私が興味を持っているのは、DelphiのDataSnapRESTサーバーでJSONPを使用する方法を誰かが理解しているかどうかです。私が見ているように、ここに問題があります。典型的な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

Delphi DataSnap RESTメソッドには、カスタムJSON処理をバイパスして、必要なJSONを正確に返す方法があります。これは、(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/ の情報。

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

于 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

からの回答は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 に答える
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 に答える