如何使跨域资源共享 (CORS) 发布请求正常工作
Posted
技术标签:
【中文标题】如何使跨域资源共享 (CORS) 发布请求正常工作【英文标题】:How to get a cross-origin resource sharing (CORS) post request working 【发布时间】:2011-08-10 16:15:57 【问题描述】:我的本地局域网 (machineA) 上有一台机器,它有两个 Web 服务器。第一个是 XBMC 中的内置库(在端口 8080 上)并显示我们的库。第二个服务器是一个 CherryPy python 脚本(端口 8081),我用它来按需触发文件转换。文件转换由来自 XBMC 服务器提供的页面的 AJAX POST 请求触发。
转到显示库的http://machineA:8080 显示库 用户点击“转换”链接发出以下命令 -jQuery Ajax 请求
$.post('http://machineA:8081', file_url: 'asfd', function(d)console.log(d))
浏览器发出带有以下标头的 HTTP OPTIONS 请求;
请求标头 - 选项
Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://machineA:8080
Access-Control-Request-Method: POST
Access-Control-Request-Headers: x-requested-with
服务器响应如下;
响应标头 - OPTIONS(状态 = 200 OK)
Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:40:29 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1
然后谈话停止。理论上,浏览器应该发出 POST 请求,因为服务器使用正确的 (?) CORS 标头进行响应(Access-Control-Allow-Origin: *)
为了排除故障,我还从http://jquery.com 发出了相同的 $.post 命令。这是我被难住的地方,来自 jquery.com,post 请求有效,OPTIONS 请求在 POST 之后发送。此交易的标头如下;
请求标头 - 选项
Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Origin: http://jquery.com
Access-Control-Request-Method: POST
响应标头 - OPTIONS(状态 = 200 OK)
Content-Length: 0
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: text/html;charset=ISO-8859-1
请求标头 - POST
Host: machineA:8081
User-Agent: ... Firefox/4.01
Accept: */*
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 115
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://jquery.com/
Content-Length: 12
Origin: http://jquery.com
Pragma: no-cache
Cache-Control: no-cache
响应标头 - POST(状态 = 200 OK)
Content-Length: 32
Access-Control-Allow-Headers: *
Access-Control-Max-Age: 1728000
Server: CherryPy/3.2.0
Date: Thu, 21 Apr 2011 22:37:59 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Content-Type: application/json
我不明白为什么同一个请求可以从一个站点运行,但不能从另一个站点运行。我希望有人能够指出我所缺少的。感谢您的帮助!
【问题讨论】:
如果两个 Web 服务器在同一台机器上,是否需要 CORS? 据我所知,这是一个 CORS 请求,因为端口不同。此外,OPTIONS 请求表明浏览器将其视为 CORS 请求 A CORS POST request works from plain javascript, but why not with jQuery?的可能重复 【参考方案1】:我终于偶然发现了这个链接“A CORS POST request works from plain javascript, but why not with jQuery?”,它指出 jQuery 1.5.1 添加了
Access-Control-Request-Headers: x-requested-with
所有 CORS 请求的标头。 jQuery 1.5.2 不这样做。另外,根据同一个问题,设置服务器响应头为
Access-Control-Allow-Headers: *
不允许继续响应。您需要确保响应标头特别包含所需的标头。即:
Access-Control-Allow-Headers: x-requested-with
【讨论】:
【参考方案2】:请求:
$.ajax(
url: "http://localhost:8079/students/add/",
type: "POST",
crossDomain: true,
data: JSON.stringify(somejson),
dataType: "json",
success: function (response)
var resp = JSON.parse(response)
alert(resp.status);
,
error: function (xhr, status)
alert("error");
);
回应:
response = HttpResponse(json.dumps('"status" : "success"'))
response.__setitem__("Content-type", "application/json")
response.__setitem__("Access-Control-Allow-Origin", "*")
return response
【讨论】:
如果服务器不接受跨域,crossdomain=true 不必解决问题。使用 dataType:"jsonp" 并设置像 jsonpCallback: "response" 这样的回调会是更好的主意。另见:api.jquery.com/jquery.ajax【参考方案3】:通过使用 Jquery ajax 设置我的请求标头,我在使用 google 距离矩阵 API 时解决了我自己的问题。看看下面。
var settings =
'cache': false,
'dataType': "jsonp",
"async": true,
"crossDomain": true,
"url": "https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=place_id:"+me.originPlaceId+"&destinations=place_id:"+me.destinationPlaceId+"®ion=ng&units=metric&key=mykey",
"method": "GET",
"headers":
"accept": "application/json",
"Access-Control-Allow-Origin":"*"
$.ajax(settings).done(function (response)
console.log(response);
);
注意我在设置中添加的内容 **
"headers":
"accept": "application/json",
"Access-Control-Allow-Origin":"*"
** 我希望这会有所帮助。
【讨论】:
这是唯一适用于我们的解决方案,无需更改服务器端的任何内容...thanx miracool @Timsta,很高兴我的建议对你有用。也感谢堆栈溢出。祝你有美好的一天。【参考方案4】:我花了一些时间才找到解决方案。
如果您的服务器响应正确并且请求是问题,您应该在请求中将withCredentials: true
添加到xhrFields
:
$.ajax(
url: url,
type: method,
// This is the important part
xhrFields:
withCredentials: true
,
// This is the important part
data: data,
success: function (response)
// handle the response
,
error: function (xhr, status)
// handle errors
);
注意:jQuery >= 1.5.1 是必需的
【讨论】:
jquery 版本好吗?你设置withCredentials: true
了吗?你确定你有相关的标题?
是的,1withCredential: true,
jquery version: 3.2.1`。实际上它是通过邮递员工作的,但它没有通过 chrome 浏览器
我几乎可以肯定邮递员不应该有 CORS 问题,因为它不是浏览器并且它的行为是不同的。你确定你有正确和相关的头从服务器发送到客户端吗?再一次 - 请注意,这种变化还不够。您需要确保服务器响应带有 正确 标头。
你能告诉我服务器上需要什么响应头吗?
@MoxShah 这是一个完全不同的问题,那里有很多资源:)【参考方案5】:
好吧,我在这个问题上苦苦挣扎了几个星期。
最简单、最合规且非 hacky 的方法可能是使用提供者 JavaScript API,它不会进行基于浏览器的调用,并且可以处理跨域请求。
例如Facebook JavaScript API 和 Google JS API。
如果您的 API 提供程序不是最新的,并且在其响应中不支持 Cross Origin Resource Origin '*' 标头并且没有 JS api(是的,我说的是 Yahoo ),您会被以下三个之一震惊选项-
在您的请求中使用 jsonp 会向您的 URL 添加一个回调函数,您可以在其中处理您的响应。 请注意,这将更改请求 URL,因此您的 API 服务器必须能够处理 URL 末尾的 ?callback=。
将请求发送到您的 API 服务器,该服务器是您的控制器,并且与客户端位于同一域中,或者启用了跨源资源共享,您可以将请求代理到第 3 方 API 服务器。
在您发出 OAuth 请求并需要处理用户交互的情况下可能最有用 哈哈! window.open('url',"newwindowname",'_blank', 'toolbar=0,location=0,menubar=0')
【讨论】:
【参考方案6】:将它与 Laravel 结合使用解决了我的问题。只需将此标头添加到您的 jquery 请求 Access-Control-Request-Headers: x-requested-with
并确保您的服务器端响应具有此标头集 Access-Control-Allow-Headers: *
。
【讨论】:
没有理由手动将 CORS 标头添加到请求中。浏览器将始终为您添加 prop CORS 标头到请求中。 真正的挑战是让服务器回复正确的Access-Control-Allow-Headers
,而JQ 提供正确的Access-Control-Request-Headers
(加上您通过代码添加的任何内容)两者都不能是通配符。只需要一个“坏”的标题就可以在飞行前炸毁,例如如果服务器没有列出,则使用If-None-Match
进行条件GET。【参考方案7】:
这是对我有用的总结:
定义一个新函数(包装$.ajax
以简化):
jQuery.postCORS = function(url, data, func)
if(func == undefined) func = function();
return $.ajax(
type: 'POST',
url: url,
data: data,
dataType: 'json',
contentType: 'application/x-www-form-urlencoded',
xhrFields: withCredentials: true ,
success: function(res) func(res) ,
error: function()
func()
);
用法:
$.postCORS("https://example.com/service.json", x : 1 ,function(obj)
if(obj.ok)
...
);
也适用于.done
、.fail
等:
$.postCORS("https://example.com/service.json", x : 1 ).done(function(obj)
if(obj.ok)
...
).fail(function()
alert("Error!");
);
服务器端(在本例中托管 example.com),设置这些标头(在 php 中添加了一些示例代码):
header('Access-Control-Allow-Origin: https://not-example.com');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 604800');
header("Content-type: application/json");
$array = array("ok" => $_POST["x"]);
echo json_encode($array);
这是我知道真正从 JS 跨域 POST 的唯一方法。
JSONP 将 POST 转换为 GET,可能会在服务器日志中显示敏感信息。
【讨论】:
【参考方案8】:由于某种原因,一个关于 GET 请求的问题被合并到这个问题中,所以我会在这里回复它。
这个简单的函数将从启用 CORS 的页面异步获取 HTTP 状态回复。如果您运行它,您将看到如果通过 XMLHttpRequest 访问,只有具有正确标题的页面才会返回 200 状态——无论使用的是 GET 还是 POST。如果您只需要一个 json 对象,除了可能使用 JSONP 之外,客户端无法解决此问题。
可以轻松修改以下内容以获取 xmlHttpRequestObject 对象中保存的数据:
function checkCorsSource(source)
var xmlHttpRequestObject;
if (window.XMLHttpRequest)
xmlHttpRequestObject = new XMLHttpRequest();
if (xmlHttpRequestObject != null)
var sUrl = "";
if (source == "google")
var sUrl = "https://www.google.com";
else
var sUrl = "https://httpbin.org/get";
document.getElementById("txt1").innerHTML = "Request Sent...";
xmlHttpRequestObject.open("GET", sUrl, true);
xmlHttpRequestObject.onreadystatechange = function()
if (xmlHttpRequestObject.readyState == 4 && xmlHttpRequestObject.status == 200)
document.getElementById("txt1").innerHTML = "200 Response received!";
else
document.getElementById("txt1").innerHTML = "200 Response failed!";
xmlHttpRequestObject.send();
else
window.alert("Error creating XmlHttpRequest object. Client is not CORS enabled");
<html>
<head>
<title>Check if page is cors</title>
</head>
<body>
<p>A CORS-enabled source has one of the following HTTP headers:</p>
<ul>
<li>Access-Control-Allow-Headers: *</li>
<li>Access-Control-Allow-Headers: x-requested-with</li>
</ul>
<p>Click a button to see if the page allows CORS</p>
<form name="form1" action="" method="get">
<input type="button" name="btn1" value="Check Google Page" onClick="checkCorsSource('google')">
<input type="button" name="btn1" value="Check Cors Page" onClick="checkCorsSource('cors')">
</form>
<p id="txt1" />
</body>
</html>
【讨论】:
【参考方案9】:我遇到了完全相同的问题,jquery ajax 只在获取请求工作正常的发布请求上给了我 cors 问题 - 我厌倦了上面的所有内容而没有结果。我的服务器等中有正确的标头。改用 XMLHTTPRequest 而不是 jquery 立即解决了我的问题。无论我使用哪个版本的 jquery,它都没有修复它。如果您不需要向后浏览器兼容性,Fetch 也可以正常工作。
var xhr = new XMLHttpRequest()
xhr.open('POST', 'https://mywebsite.com', true)
xhr.withCredentials = true
xhr.onreadystatechange = function()
if (xhr.readyState === 2) // do something
xhr.setRequestHeader('Content-Type', 'application/json')
xhr.send(json)
希望这可以帮助遇到同样问题的其他人。
【讨论】:
【参考方案10】:如果由于某些原因在尝试添加标头或设置控制策略时您仍然无处可去,您可以考虑使用 apache ProxyPass...
例如,在一个使用 SSL 的 <VirtualHost>
中添加以下两个指令:
SSLProxyEngine On
ProxyPass /oauth https://remote.tld/oauth
确保已加载以下 apache 模块(使用 a2enmod 加载它们):
代理 proxy_connect proxy_http显然您必须更改您的 AJAX 请求 url 才能使用 apache 代理...
【讨论】:
【参考方案11】:这对聚会来说有点晚了,但我已经为此苦苦挣扎了几天。这是可能的,我在这里找到的答案都没有奏效。这看似简单。 这是 .ajax 调用:
<!DOCTYPE HTML>
<html>
<head>
<body>
<title>Javascript Test</title>
<script src="http://code.jquery.com/jquery-latest.min.js"></script>
<script type="text/javascript">
$(document).domain = 'XXX.com';
$(document).ready(function ()
$.ajax(
xhrFields: cors: false,
type: "GET",
url: "http://XXXX.com/test.php?email='steve@XXX.com'",
success: function (data)
alert(data);
,
error: function (x, y, z)
alert(x.responseText + " :EEE: " + x.status);
);
);
</script>
</body>
</html>
这是服务器端的 php:
<html>
<head>
<title>PHP Test</title>
</head>
<body>
<?php
header('Origin: xxx.com');
header('Access-Control-Allow-Origin:*');
$servername = "sqlxxx";
$username = "xxxx";
$password = "sss";
$conn = new mysqli($servername, $username, $password);
if ($conn->connect_error)
die( "Connection failed: " . $conn->connect_error);
$sql = "SELECT email, status, userdata FROM msi.usersLive";
$result = $conn->query($sql);
if ($result->num_rows > 0)
while($row = $result->fetch_assoc())
echo $row["email"] . ":" . $row["status"] . ":" . $row["userdata"] . "<br>";
else
echo " ";
$conn->close();
?>
</body>
【讨论】:
不管怎样,Origin 标头是请求标头,而不是响应标头。你的 php 脚本不应该设置它。 嗯。当我看到您在代码中创建 AJAX“GET”时,这让我的希望破灭了,而 OP 很清楚地表示他试图避免使用“GET”并想使用“POST”。跨度> CORS 标头的工作方式与所涉及的动词无关。我们可以通过$.ajax()
对正确配置的服务器调用所有动词。最难的部分是让Access-Control-Request-Headers
正确,但即使这样也不是太难。正如之前的海报所指出的,这不能是通配符,而是标题的白名单。以上是关于如何使跨域资源共享 (CORS) 发布请求正常工作的主要内容,如果未能解决你的问题,请参考以下文章