Есть ли способ использовать 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;