跨域 AJAX 请求不起作用

Posted

技术标签:

【中文标题】跨域 AJAX 请求不起作用【英文标题】:Cross-domain AJAX request not working 【发布时间】:2014-04-18 20:10:47 【问题描述】:

我正在通过 jQuery 的 $.ajax 函数在我一直使用的第三方 API 上调用 POST。但是,当我拨打电话时,出现以下错误:XMLHttpRequest cannot load http://the-url.com. The request was redirected to 'http://the-url.com/anotherlocation', which is disallowed for cross-origin requests that require preflight.

我从this post 看到这可能是一个 Webkit 错误,所以我在 Firefox 中尝试了它(我正在使用 Chrome 进行开发),我得到了相同的结果。我在 Chrome 和 Firefox 上尝试了这个,我得到了同样的结果。

根据this post,我还尝试通过将$.ajax 函数的crossDomain 属性设置为true 并将dataType 设置为jsonp 来使用jsonp。但是,这导致了 500 内部服务器错误。

当我使用 --disable-web-security 标志启动 Chrome 时,我没有任何问题。但是,如果我正常启动浏览器,则会收到错误消息。

所以,我想这可能是一个由两部分组成的问题。我该怎么做才能发出这个跨域请求?如果 JSONP 是答案,那么我该如何确定第三方 API 是否设置正确以支持此功能?

编辑:这是我在禁用浏览器安全性的情况下拨打电话时的屏幕截图:https://drive.google.com/file/d/0Bzo7loNBQcmjUjk5YWNWLXM2SVE/edit?usp=sharing

这是我在启用浏览器安全性的情况下拨打电话时的屏幕截图(与正常情况一样):https://drive.google.com/file/d/0Bzo7loNBQcmjam5NQ3BKWUluRE0/edit?usp=sharing

【问题讨论】:

【参考方案1】:

关于你的第一个问题,

我该怎么做才能发出这个跨域请求?

首先将以下请求标头发送到服务器。根据响应标头,当标头不相加时,UserAgent(即此处的浏览器)将丢弃响应(如果有)并且不将其返回给 XHR 回调。

Origin: http://yourdomain.com Access-Control-Request-Method: POST Access-Control-Request-Headers: X-Custom-Header

然后,您的服务器应使用以下标头进行响应:

Access-Control-Allow-Origin: http://yourdomain.com Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: X-Custom-Header

示例取自 - https://***.com/a/8689332/1304559

您可以使用 Fiddler 或 Web Inspector 网络选项卡 (Chrome) 或 Firebug 的网络选项卡等工具来查找服务器为响应您的请求而发回的标头。

第三方 API 服务器 url 应返回支持的值。如果不是,那是你的问题。

对 JsonP 500 服务器错误没有太大帮助,正如它所说的 Internal Server Error


更新

我看到了你的屏幕截图,这里有几点需要注意

    使用HTTP POST 在两种模式响应中都找不到Access-Control 标头。第二个状态码为302 Origin 请求标头是 null,所以我相信 html 文件是从文件系统而不是从网站打开的

启用 JSONP

说到为什么 JSONP 不起作用,原因 - Web 服务配置(指定的 ASMX)没有为请求启用 GET 模式。 JSONP 不适用于 POST

为什么 200 禁用了安全功能?

POST 请求被直接调用,没有OPTIONS 或在它之前触发预检请求,因此服务器只是响应。

为什么302 状态码启用了安全性?

首先介绍一下未禁用安全标志(默认)时的情况。 OPTIONS 请求被触发到 URL。 URL 应该返回一个可以使用的 HTTP 方法列表,即GETPOST 等。这个OPTIONS 请求的状态码应该是200

在当前情况下,执行此操作时会调用重定向。并且重定向指向 HTTP 服务器错误的自定义错误处理程序。我相信是404错误,正在被捕获并重定向。[这可能是因为自定义处理程序设置为ResponseRedirect而不是ResponseRewrite]404的原因是跨域访问不是已启用。

如果自定义错误处理被关闭,这将返回HTTP 500 代码。我相信可以通过在触发跨域请求后立即检查服务器日志来找到问题。最好检查服务器日志中是否存在除此之外的错误(如果有)。

可能的解决方案

要启用访问,有两种方法 - detailed here。或者直接将访问控制头添加到web.config文件的customheaders部分。

启用跨域访问后,服务器应响应 OPTIONS 并允许请求通过。这应该返回HTTP 200,除非有其他错误。但是 ajax 回调将无法访问响应。仅当Access-Control-Allow-Origin 设置为“*”或Origin 请求标头值,并且根据需要设置Access-Control 时,现在才能执行此操作。 ajax 回调将按预期接收响应。

第三点,空Origin。如果Origin 请求标头值作为Access-Control-Allow-Origin 发回,这将是一个问题。但我假设您会将 HTML 页面移动到 Web 服务器,所以这应该不是问题。

希望这会有所帮助!

【讨论】:

谢谢。这很有帮助。我查看了 Web Inspector 的 Network 选项卡中的网络调用,但没有看到必要的 Access-Control 标头。我想这可能是问题所在。如果标题在那里,它们会显示在 Web Inspector 的“网络”选项卡中,对吗? 是的,没错。您可以在网络选项卡上尝试从 HTML 页面中的 CDN 下载脚本,例如:ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js 同样,第三方 API 应根据需要启用访问控制标头。 好的。我明白你在说什么,我正在考虑联系 API 的提供者,看看他们为什么不支持 JSONP。我将网络选项卡的屏幕截图附加到问题中。请你看看,让我知道你的想法吗? 没问题!这将有助于更好地理解问题 我查看了您的答案,但不幸的是,我无权访问服务器。因此,我提出的解决方案类似于@waki 发布的解决方案,但稍作修改以支持 SOAP。再次感谢您的帮助!【参考方案2】:

我想出的解决方案是使用 cURL(正如 @waki 所提到的),但它是支持 SOAP 的稍作修改的版本。然后,我没有对第三方 API 进行 AJAX 调用(配置不正确),而是调用我的本地 php 文件,该文件然后对第三方 API 进行 SOAP 调用并将数据传递回我的 PHP 文件,我可以然后处理它。这让我忘记了 CORS 以及与之相关的所有复杂性。这是代码(取自this问题并进行了修改,但没有经过身份验证)。

$post_data = "Some xml here";
$soapUrl = "http://yoursite.com/soap.asmx"; // asmx URL of WSDL


$headers = array(
    "Content-type: text/xml;charset=\"utf-8\"",
    "Accept: text/xml",
    "Cache-Control: no-cache",
    "Pragma: no-cache",
    "SOAPAction: http://yoursite.com/SOAPAction",
    "Content-length: " . strlen($post_data),
); //SOAPAction: your op URL

$url = $soapUrl;

// PHP cURL
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); // the SOAP request
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$response = curl_exec($ch);

/* Check for an error when processing the request. */
if(curl_errno($ch) != 0) 
   // TODO handle the error


curl_close($ch);

// TODO Parse and process the $response variable (returned as XML)

【讨论】:

这可行,但我将其称为“解决方法”而不是解决方案。有些人在某些情况下只能依赖前端,而没有使用后端代理的选项。 是的,我希望有更好的解决方案。但是,由于安全性的限制并且无法控制服务器,因此通过我控制的服务器代理它是我遇到的最佳解决方案。【参考方案3】:

你可以使用 cURL 吗? 使用 Ajax,您可以将数据发送到您的处理程序(在您的服务器上,让我们调用 callback.php)。 在您的文件 (callback.php) 中使用 cURL 和您的数据:

$post_data = array( 
    'key' => 'key', 
    'test' => 'text'
    ); 

$curl = curl_init(); 
curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); 
curl_setopt($curl, CURLOPT_POST, TRUE); 
curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);  
curl_setopt($curl, CURLOPT_URL, 'http://www.domain.com/'); 
$return = json_decode(trim(curl_exec($curl)), TRUE); 
curl_close($curl); 

在您的 $return 变量中,您可以获取数据。

【讨论】:

很好的建议,谢谢!实际上,我使用了支持 SOAP 的稍微修改过的版本。

以上是关于跨域 AJAX 请求不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Ruby on Rails - AJAX 请求不起作用(跨域),尝试了一切

具有跨域 Ajax 的 WCF Web 服务不起作用

Jquery ajax()跨域远程服务器在IE8中不起作用[重复]

用于基本授权的 ajax 设置标头不起作用

跨域 Ajax 调用在 Safari 4 和 Safari 5 中不起作用

来自本地文件的jQuery ajax POST访问跨域不起作用