Delphi 的 Datasnap ISAPI 模块上的 CORS 问题
Posted
技术标签:
【中文标题】Delphi 的 Datasnap ISAPI 模块上的 CORS 问题【英文标题】:CORS issue on a Delphi's Datasnap ISAPI Module 【发布时间】:2016-12-11 16:17:08 【问题描述】:AngularJS 客户端访问的 Datasnap REST (Delphi 10.1 Berlin) 服务器存在问题。我无法激活授权,因为 Angular 无法在 Pragma Header 中发送 dssession,这似乎是 CORS 的一个问题,因为浏览器是更改该 Header 的那个(使用 --disable-web-security flat all运行良好)。
即使在同一台机器上运行 Angular 和 Datasnap 进行测试(Angular 在 localhost:8080 和 Datasnap 在 localhost:8081),浏览器也会将调用检测为跨域调用,并且当 Angular 尝试发送 dssession 时它不会到达Datasnap。注意:我允许使用以下代码进行跨域调用:代码http://delphi.org/2015/04/cors-on-datasnap-rest-server/
将服务器作为独立应用程序运行我可以在 WebModuleBeforeDispatch 事件中看到 TWebRequest 获取值为“Pragma”而不是预期的 Pragma Header 的 Access-Control-Request-Headers,因此看起来浏览器正在发出一个 CORS 选项请求并且 Datasnap 没有回答它(它引发一个 TDSServiceException 并带有“命令关闭或未分配”消息)。
我已经为 StandAlone 应用程序解决了这个问题,通过 URL 传递 dssession(它不会干扰参数的正常传递,因为我只使用来自 AngularJS 的 POST 调用),然后在 WebModuleBeforeDispatch 事件和使用从调用 URL 检索到的 dssession 手动添加 Pragma Header。
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest;
Response: TWebResponse; var Handled: Boolean);
var Token: string;
begin
Response.SetCustomHeader('Access-Control-Allow-Origin','*'); // Allow CORS calls
Token := TIdHTTPAppRequest(Request).Query; // Set session on Pragma from the URL
if Copy(Token, 1, 10) = 'dssession=' then begin
TIdHTTPAppRequest(Request).GetRequestInfo.RawHeaders.AddValue('Pragma', Token);
end;
if FServerFunctionInvokerAction <> nil then
FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;
它在那个 StandAlone 应用程序上运行良好,但是当我将我的代码重新编译为 ISAPI 模块以将其部署到最终生产环境中时,它不会在请求中添加 dssession Pragma 标头,可能是因为它没有得到dssession 通过 URL 传递,但我无法确定原因,因为我无法让我的 Delphi 调试该 ISAPI 模块。
我遵循本教程:http://edn.embarcadero.com/article/40873 并且可以正确设置为运行我的 ISAPI 模块,但是当我将 w3wp.exe 进程附加到我的 Delphi 调试器时,它不会停止到任何断点(它们似乎已禁用,就像代码是使用 Release Build 而不是 Debug Build 编译的一样),事实上,w3wp.exe 进程似乎被冻结并且在我将它与 Delphi 调试器分离之前不会参与任何调用。
因此,如果您有任何建议能够调试该模块,更重要的是,当您的浏览器将 dssession 检测为跨域调用时将其传递给 ISAPI 模块,我将不胜感激。
非常感谢。
【问题讨论】:
服务器 CORS 支持是否包括预检处理?您提供的链接上的最后两个 cmets 不建议 我找不到任何关于它的文档,但实际上,Datasnap 似乎不支持预检处理 :-((这就是为什么我试图避免它,而不是使用自定义标题 Pragma 并发送通过 URL 的 dssession)。 我对 Angular 的了解还不够,但如果它确实是所有客户端 (javascript) 文件,为什么不从同一个 IIS 实例托管这两个文件呢?可选择使用虚拟目录。跨域检查不会触发。 我们是两家不同的公司,负责前端和后端的编码,我们的老板希望将他们的代码放在自己的服务器上。 :-( 【参考方案1】:我终于找到了一个巧妙的解决方案,可以将 Datasnap 设置为响应 CORS 请求,因为它应该响应它们。
当您的 Datasnap 在 WebModule 上收到 COR 请求时,在调度事件之前,您只需回答允许发送自定义标头 (Pragma),将 Handled 设置为 True 很重要,因此 Datasnap 不会尝试管理它OPTION 请求作为调用方法的普通请求。
procedure TWebModule1.WebModuleBeforeDispatch(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
Response.SetCustomHeader('Access-Control-Allow-Origin','*');
if Trim(Request.GetFieldByName('Access-Control-Request-Headers')) <> '' then
begin
Response.SetCustomHeader('Access-Control-Allow-Headers', Request.GetFieldByName('Access-Control-Request-Headers'));
Handled := True;
end;
if FServerFunctionInvokerAction <> nil then
FServerFunctionInvokerAction.Enabled := AllowServerFunctionInvoker;
end;
【讨论】:
在我的环境中(Delphi XE2、简单的 WebBroker ISAPI 模块、带有 mod_isapi 的 apache 2.4)我不得不将 Access-Control-Request-Headers 更改为 Access_Control_Request_Headers,因为在 Delphi XE2 TISAPIRequest.GetFieldByName(... ) 使用 HTTP_ 前缀而不是 HEADER_。对于 HTTP_ 和 HEADER_,请参阅 msdn.microsoft.com/en-us/library/… 我还添加了 if Request.Method = 'OPTIONS' then Handled = true 来处理任何飞行前请求以上是关于Delphi 的 Datasnap ISAPI 模块上的 CORS 问题的主要内容,如果未能解决你的问题,请参考以下文章
delphi 中的datasnap 是啥意思 Developing Datasnap Application 中文意思是啥