跨域http请求
当两个站点的协议(http、https、ftp)、域名、端口不同时,该两个站点构成跨域。出于安全原因, 浏览器会限制脚本发起跨域http请求(也有可能是正常发送,但是拦截返回值)。实际项目中常用的解决跨域的方法通常有JSONP、CORS等。
使用CORS跨域
JSONP跨域比较简单,不需要服务器端配合,但是只能发送GET请求;CORS是目前使用最为广泛的跨域解决方案,需要服务端的配合。
简单请求与非简单请求
浏览器将CORS分为简单请求和非简单请求,非简单请求需要首先发送CORS预检请求,相当于发送两次请求,当预检请求成功后,才发送真正的http请求.
简单请求
若请求满足所有下述条件,则该请求可视为“简单请求”
使用下列方法:
- GET
- POST
- HEAD
http的首部仅能包含以下:
- Accept
- Accept-Language
- Content-Language
- Content-Type (text/plain、multipart/form-data、application/x-www-form-urlencoded)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
客户端和服务器之间使用 CORS 首部字段来处理跨域权限:
请求头部:
GET /resources/public-data/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
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
Connection: keep-alive
Referer: http://foo.example/examples/access-control/simpleXSInvocation.html
Origin: http://foo.example
响应头部:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml
[XML Data]
> 请求头部中的Origin字段表示请求来源这个域,与响应头部中的Access-Control-Allow-Origin字段形成简单访问控制。
非简单请求
非简单请求需要首先发送预检请求,预检请求就是使用options方法(http的一种方法),发起一个预检请求到服务器,确认服务器是否该实际请求;如果服务器否定了“预检”请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。
当请求满足下述任一条件时,即应首先发送预检请求:
使用下列方法:
1. PUT
2. DELETE
3. CONNECT
4. OPTIONS
5. TRACE
6. PATCH
http的首部仅能包含以下:
1. Accept
2. Accept-Language
3. Content-Language
4. Content-Type (不为text/plain、multipart/form-data、application/x-www-form-urlencoded)
5. DPR
6. Downlink
7. Save-Data
8. Viewport-Width
9. Width
如下是一个需要执行预检请求的 HTTP 请求:
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
}
上述代码中,使用了自定义的http头信息“X-PINGOTHER”,而且Content-Type为application/xml,所以需要发送预检请求。
首先通过options方法发送预检请求,预检请求验证通过后发送真正的请求,最后完成http请求。
附带身份凭证的请求
CORS可以基于HTTP cookies和HTTP发送身份凭证,一般对于跨域的XMLHttpRequest请求,浏览器不会发送身份凭证信息,如果需要发送则需要设置标志位(withCredentials = true)
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true; //向服务器发送cookies
invocation.onreadystatechange = handler;
invocation.send();
}
}
如果服务器端的响应中未携带 Access-Control-Allow-Credentials: true ,浏览器将不会把响应内容返回给请求的发送者。
参考文献
[1]:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS