通过CORS进行跨域Ajax请求
在XMLHttpRequest中,跨域请求是不被允许的,通常会使用jsonp等方案进行跨域。但是现行标准可以利用CORS(Cross-origin resource sharing)方案进行跨域请求。但是请注意这种方案存在兼容问题,IE8不能正常使用。所以如果考虑兼容还是jsonp更好一点。
在做跨域ajax的请求时,客户端和服务器都需要进行专门的设置。
简单请求和预检请求
某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”。请求满足下列所有条件则认定为简单请求。
1.使用下列方法之一:
GET
HEAD
POST
2.除了被用户代理自动设置的首部字段(例如 Connection ,User-Agent)和在 Fetch 规范中定义为 禁用首部名称 的其他首部,允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:
Accept
Accept-Language
Content-Language
Content-Type (需要注意额外的限制)
DPR
Downlink
Save-Data
Viewport-Width
Width
3.Content-Type 的值仅限于下列三者之一:
text/plain
multipart/form-data
application/x-www-form-urlencoded
4.请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。 5.请求中没有使用 ReadableStream 对象。
与前述简单请求不同,“需预检的请求”要求必须首先使用 OPTIONS
方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
服务端
跨域的允许主要由服务器端控制,服务器端通过在响应的 header 中设置 Access-Control-Allow-Origin
及相关一系列参数,提供跨域访问的允许策略。
服务端设置
Access-Control-Allow-Origin
该头选项设置了同源安全策略。可以设置为 *
,但是副作用是客户端发送请求时不会携带Cookie
。
通常可以设置为以下选项。
Access-Control-Allow-Origin: http://127.0.0.1
Access-Control-Allow-Origin: http://127.0.0.1:8080
Access-Control-Allow-Origin: https://127.0.0.1
Access-Control-Allow-Origin: http://.example.com
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials
是否允许后续请求携带认证信息(cookies
),该值只能是true
,否则不返回。
通常可以设置为以下选项。
Access-Control-Allow-Credentials: true
Access-Control-Allow-Methods
在对预检请求(preflight request
)的应答中明确了客户端所要访问的资源允许使用的方法或方法列表。
通常可以设置为以下选项。
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Methods: GET
Access-Control-Max-Age
设定预检请求(preflight request
)的有效期,单位为秒。在此期间不用发出另一条预检请求。
注意:不同的浏览器缓存时间上限并不相同。当值为-1
时,禁用缓存。
通常可以设置为以下选项。
Access-Control-Max-Age: 600
Access-Control-Max-Age: -1
Access-Control-Allow-Headers
用于预检请求(preflight request
)中,列出了将会在正式请求的 Access-Control-Request-Headers
字段中出现的首部信息。
注意以下这些特定的首部是一直允许的:Accept, Accept-Language, Content-Language, Content-Type (但只在其值属于 MIME 类型 application/x-www-form-urlencoded, multipart/form-data 或 text/plain中的一种时)。这些被称作simple headers,你无需特意声明它们。
通常可以设置为以下选项。
Access-Control-Allow-Headers: X-Custom-Header
Access-Control-Allow-Headers: X-Custom-Header, Upgrade-Insecure-Requests
服务端代码示例
此处使用PHP代码作为示例,对于其他语言一样通用。
<?php
/**
这段代码启用了cros,但只允许http://127.0.0.1请求该域的资源。
也就是说客户端当时访问的网页的域必须是http://127.0.0.1。域名端口和协议都不能变。
设置了只允许GET, POST, PUT, DELETE, OPTIONS请求。且开启了Cookie。
还允许了自定义HTTP头mysessid的提交。
*/
header("Access-Control-Allow-Credentials: true");
header("Access-Control-Allow-Origin: http://127.0.0.1");
header("Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS");
header('Access-Control-Allow-Headers:x-requested-with,content-type,mysessid');
客户端
跨域的请求必然需要客户端的配合。
客户端设置
Access-Control-Request-Headers
出现于预检请求(preflight request
)中,用于通知服务器在真正的请求中会采用哪些请求头。也就是客户端请求头会有哪些。
Access-Control-Request-Methods
出现在预检请求(preflight request
)用于通知服务器在真正的请求中会采用哪种 HTTP 方法。
客户端代码示例
此处使用jquery的ajax请求,原生js同理。
$.ajax({
url: "http://127.0.0.1/",
type: 'GET',
crossDomain: true,
beforeSend: function(req) {
req.setRequestHeader("mysessid", ""); //这里传输了自定义头mysessid
},
xhrFields: {
withCredentials: true // 这里设置了withCredentials,表明传输cookie
},
success: function(data) {
console.log(data)
},
error: function(err) {
console.error(err)
}
})
注意事项
有几个注意事项需要注意一下。
1. 避免混乱
Access-Control-Allow-Headers 和 Access-Control-Request-Headers
前者属于服务端设置,应该在响应头里。
后者属于客户端设置,应该在请求头里。
如果出现在对方的位置,一定是错误的设置,其他头同理。
2. Cookie和自定义HTTP头的使用
Cookie的传递和自定义HTTP头收多个头相互影响,在这点要多加注意。