Есть ли способ использовать JSONP с сервером Delphi DataSnap REST?

похоже, что нет способа реализовать JSONP (JSON с заполнением) решение с использованием DataSnap, но я хочу бросить этот вопрос здесь, Если кто-то решил эту проблему.

Background: JSONP-это механизм, который использует возможность перекрестной ссылки на сайт элемента HTML-скрипта для преодоления той же политики происхождения класса XmlHttpRequest. Используя XmlHttpRequest, вы можете получить данные (объекты JSON) только из того же домена, который служил HTML-документ. Но что, если вы хотите получить данные с нескольких сайтов и привязать эти данные к элементам управления в браузере?

С JSONP ваш атрибут src элемента script не ссылается на файл JavaScript, а вместо этого ссылается на веб-метод (тот, который может находиться в другом домене, из которого был получен HTML). Этот веб-метод возвращает JavaScript.

тег скрипта предполагает, что возвращаемые данные являются файлом JavaScript и выполняет его обычно. Однако на самом деле веб-метод возвращает вызов функции с литеральным объектом JSON в качестве параметра. Предполагая, что вызываемая функция определена, функция выполняет и может работать с объектом JSON. Например, функция может извлекать данные из объекта JSON и привязывать их к текущему документу.

аргументы за и против JSONP широко обсуждались (это представляет собой очень серьезную проблему безопасности), поэтому нет необходимости повторять вот здесь.

что меня интересует, если кто-нибудь там выяснил, как использовать JSONP с серверами DATASNAP REST Delphi. Вот в чем проблема, как я ее вижу. Типичное использование JSONP может включать тег сценария, который выглядит примерно так:

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

веб-метод getdata вернет вызов примерно следующим образом:

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 ответов


в методах Delphi DataSnap REST есть способ обойти пользовательскую обработку JSON и вернуть именно тот JSON, который вы хотите. Вот функция класса, которую я использую (в моей Relax framework) для возврата простых данных в 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/.

кстати, вы можете назначить nil результату функции REST.


вы можете написать потомок 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;

проблема политики происхождения может быть легко решена в 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;

ответ от 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;