bzdww

Get answers and suggestions for various questions from here

CORS cross-domain resources to share what you should know

cms

"Before you, some guest words"

CORS cross-domain resource sharing, this topic is no stranger to everyone, eat a long time to turn the public number of deep technical good text, it should also eat a little millet porridge slippery stomach in the stomach, today we will continue to do a lot of CORS cross The topic of domain resource sharing, the big cows love the Mavericks to consolidate, put this bowl of long-term non-cooled big bowl of tea, and then carefully product.

"JSONP is straightforward when it's very cool, and CORS is a sloppy soup."

In the daily work of our front end, the common way to cross-domain is JSONP. JSONP is a way to automatically execute the callback method after obtaining the required resources through the feature of the script tag without homology restriction, and our browser is native. CORS cross-domain, is the way to obtain the cross-domain resources of the server lady by "legitimate means". Compared with JSONP, only the GET request can be implemented. CORS Dafa supports all request types, and CORS initiates the request through the ordinary XMLHttpRequest. And to get the data, there is better error handling than JSONP, then let's talk about this CORS Dafa.

“Overall overview, first set a spectrum”

Official brief: CORS (Cross-Origin Resource Sharing) cross-domain resource sharing, the main idea is to use a custom HTTP header to let the browser communicate with the server to determine whether the response is successful or not, it allows the browser to cross The source server sends the request, thereby overcoming the limitations of homology.

Privately explicit: In fact, when sending a cross-domain request to the server, the browser automatically treats the normal request and the non-ordinary request differently. Add an Origin field in the request header to tell the server the source of the request, and pass the response header returned by the server. The value of the Access-Control-Allow-Origin field is not the Origin of the request. Let's see the server let the request not be sent to this resource.

"I am the basic knowledge that I don't use very much in my work, but I am also a small life."

CORS browser support:



The browser side has received good support, so the key to implementing CORS is the server. As long as the CORS interface is implemented, cross-domain communication can be realized.

IE implementation of CORS :

XDR (XDomainRequest) was introduced in IE8. Note:

  1. The cookie will not be sent with the request and will not return with the response
  2. Can only set the Content-Type field in the request header information
  3. Cannot access response header information
  4. Only supports GET and POST requests

How to use XDR objects User XHR objects are very similar, as follows:

var xdr = new XDomainRequest();
xdr.onload = function() {
   alert(xdr.responseText);
}
xdr.onerror = function() {
   alert("error");
}
xdr.open("get", "http://www.xxx.com/yyy/");
xdr.send(null);


其他浏览器对CORS的实现

Firefox 3.5+, Safari 4+, Chorme, iOS version of Safari and Android platform WebKit all support CORS through XmlHttpRequest.

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
           console.log(xhr.responseText)
}else {
           console.log('err' + xhr.status);
}
}
};
xhr.open('get','http://www.xxx.com/zzz/',true);
xhr.send(null);


跨域XHR一些安全限制:
  1. Cannot set custom headers using setRequestHeader()
  2. Cannot send and receive cookies
  3. Calling the getAllReponseHeaders() method that gets all the header information returns an empty string

"The request is not that simple, each has her habits."

The browser divides CORS requests into two categories.

1. Simple request:

Tall version definition:

Simple requests
A simple cross-site request is one that meets all the following conditions:
The only allowed methods are: GET HEAD POST
Apart from the headers set automatically by the user agent (e.g. Connection,User-Agent, etc.), the only headers which are allowed to be manually set are:
Accept
Accept-Language
Content-Language
Content-Type
The only allowed values for the Content-Type header are:
application/x-www-form-urlencoded multipart/form-data text/plain
The general idea is to say:
  1. The request method is one of the following three methods: HEAD, GET, POST
  2. HTTP header information does not exceed the following fields: Accept, Accept-Language, Content-Language, Last-Event-ID
  3. Content-Type is limited to three values: application/x-www-form-urlencoded, multipart/form-data, text/plain

2. Non-simple request:

A non-simple request is one that has special requirements for the server. All but the above are non-simple requests. The conditions are as follows:

  1. Use any of the following HTTP methods: PUT, DELETE, CONNECT, OPTIONS, TRACE, PATCH
  2. The header fields other than the set of header fields for CORS security are artificially set. The collection is: Accept, Accept-Language, Content-Language, Content-Type (but note the additional requirements below), DPR, Downlink, Save-Data, Viewport-Width, Width
  3. The value of Content-Type does not belong to one of the following: application/x-www-form-urlencoded, multipart/form-data, text/plain

"Cross-domain requests are divided into two teams, and differential treatment is also drunk."

When discussing the "CORS treatment of different requests" part of the content, we ran a nodejs project in sync with the understanding, the paper on the soldiers finally feel shallow, to do big things still have to code!

To be more intuitive, we have a proxy for the two ports we will take next:

  1. 127.0.0.1:8081 m.zhuanzhuan.com
  2. 127.0.0.1:8082 u.58.com


客户端nodejs脚本 client.js:
var http = require('http');
var fs = require('fs');
var url = require('url');

// 创建服务器
http.createServer( function (request, response) {
// 解析请求,包括文件名
var pathname = url.parse(request.url).pathname;
// 输出请求的文件名
 console.log("Request for " + pathname + " received.");
// 读取请求的文件内容
 fs.readFile(pathname.substr(1), function (err, data) {
if (err) {
     console.log(err);
// HTTP 状态码: 404 : NOT FOUND Content Type: text/plain
     response.writeHead(404, {'Content-Type': 'text/html'});
}else{
// HTTP 状态码: 200 : OK
     response.writeHead(200, {'Content-Type': 'text/html'});
// 响应文件内容
     response.write(data.toString());
}
//  发送响应数据
   response.end();
});
}).listen(8082);
// 控制台会输出以下信息
console.log('Server running at http://u.58.com/');


服务端nodejs脚本 server.js:
var express = require('express');
var app = express();
var router = express.Router();

router.all('/getData', function(req, res, next) {
//设置允许跨域请求
var reqOrigin = req.header("origin");
  console.log(reqOrigin);
if(reqOrigin !=undefined &&
  reqOrigin.indexOf("http://u.58.com") > -1){
//设置允许 http://u.58.com 这个域响应
   res.header("Access-Control-Allow-Origin", "http://u.58.com");
   res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
   res.header("Authorization",'zhuanzhuanFe')
   res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
}
 res.json(200, {name:"转转熊",property:"Cute"});
});

app.use('/', router);

var server = app.listen(8081, function () {
 console.log("应用实例,访问地址为http://127.0.0.1:8081/");
});

console.log('Server running at http://m.zhuanzhuan.com/');


创建一个index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>test cors</title>
</head>
<body >
<button id="btn" onclick="getData()">跨域获取数据</button>
</body>
<script>
function getData(){
var xhr = new XMLHttpRequest();
   xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
         console.log(xhr.responseText)
}else {
         console.log('err' + xhr.status);
}
}
};
   xhr.open('get','http://m.zhuanzhuan.com/getData',true);
   xhr.send(null);
}

</script>
</html>


我们在控制台执行我们创建的client.js 


Access the browser u.58.com/index.html (That is our 127.0.0.1:8082), the console will see the following output:


At the same time, we also ran our server, pay attention to the last service, do not stop.


With the code in mind, there is a bottom, and then we look at the simple and non-simple requests, CORS do what it!

1. Simple request:

Follow the configuration of the getData method above:

xhr.open('get','http://m.zhuanzhuan.com/getData',true);


我们点击一下http://u.58.com/index.html页面的按钮,浏览器就会从http://u.58.com下发向http://m.zhuanzhuan.com/getData发送一个普通的GET请求

For simple cross-domain requests, the browser automatically issues a CORS request. In the request header, add an Origin field, such as the request header:


The request header RequestHeaders has an Origin field, which indicates which source the protocol came from (protocol + domain name + port)

After the server receives the request, if you do not do anything for this cross-domain request, change the server.js as follows:

router.all('/getData', function(req, res, next) {
//不做任何的处理
});


服务器接收到请求后,如果Origin指定的源不在许可范围内,服务器会返回一个正常的HTTP回应,浏览器接收到的回应头信息中没有包含Access-Control-Allow-Origin字段,那么浏览器就会抛出一个错误,被XHR的onerror函数捕捉,这种情况无法通过状态码判断,状态码可能会返回200。 


If the Origin field in the request header is the source allowed by the server, the server adds an Access-Control-Allow-Origin field to the return header of the request and assigns it to the Origin in the request header, indicating that the source is allowed to request resources. Next we restore the processing of the getData request before server.js, click the button again to initiate a cross-domain request, simulate the above scenario:

router.all('/getData', function(req, res, next) {
//设置允许跨域请求
var reqOrigin = req.header("origin");
  console.log(reqOrigin);
if(reqOrigin !=undefined && reqOrigin.indexOf("http://u.58.com") > -1){
//设置允许 http://u.58.com 这个域响应
   res.header("Access-Control-Allow-Origin", "http://u.58.com");
   res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
   res.header("Authorization",'zhuanzhuanFe');
   res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization");
}
 res.json(200, {name:"转转熊",property:"Cute"});
});


如果Origin指定的域名在许可的范围内的话,服务器返回的响应,会多出来几个头信息字段: 


The fields in the Response Headers that start with Access-Control- are all fields related to CORS, where Access-Control-Allow-Origin is an indispensable field in the cross-domain response header. The values ​​are generally requests. The value of Origin in the field indicates that the cross-domain request of the current source is allowed. It can also be set to * to indicate that the request is accepted by any source. The remaining related fields are explained in detail below.

2. Non-simple request Preflighted Request: When the request sent by the browser is a non-simple request, the browser must first use the OPTIONS method to initiate a preflight request (Rreflighted Request) to the server to know whether the server allows the cross-domain request. The use of preflight requests avoids the unintended impact of cross-domain requests on the server's user data.

We will change the request from simple request GET to non-simple request PUT, and slightly change the getData method in index.html:

xhr.open('put','http://m.zhuanzhuan.com/getData',true);


当然,我们还是要先看看对跨域请求不做任何处理的时候的状态,继续将server.js中对Access-Control-Allow-Origin字段的设置注释掉,发送下请求: 


If the Access-Control-Allow-Origin field in the response header is not set to the value of the Origin field or "*", it means that the preflight request is not approved, and a normal HTTP response is returned, but there is no CORS related header information. Field, when the browser receives the response, it will be captured by the onerror() callback function of the XMLHttpRequest object, and the console will output the error message as above.

Let's take a look at the processing logic of a normal complex request. Restore the settings of the Access-Control-Allow-Origin field in server.js, refresh the page, click the button to send the request again, we see that the page has a request of type OPTIONS more than the PUT request we are sending:


This is the preflight request. For the preflight request, there is a required field in the response header: Access-Control-Request-Method, which indicates the method that the request is allowed to use. If there is no such field, the preflight request cannot be passed. Let's practice it and comment out before responding. In the setting of the Access-Control-Request-Method field, change the server.js:

router.all('/getData', function(req, res, next) {
//设置允许跨域请求
var reqOrigin = req.header("origin");
if(reqOrigin !=undefined && reqOrigin.indexOf("http://u.58.com") > -1){
//设置允许 http://u.58.com 这个域响应
   res.header("Access-Control-Allow-Origin", "http://u.58.com");
}
 res.json(200, {name:"转转熊",property:"Cute"});
});


点击页面按钮发送put请求,可以看到页面控制台报错: 


As the name implies, the put method we sent is not allowed in the response header, so the preflight request fails and the real request is killed in the cradle...

Come and let us work on the programmer's card to save the giant baby in the cradle with the fingers of the world! Restore server.js to its original state and resend the request:


Once the server passes the preflight request, every time the normal CORS request of the browser will be the same as the simple request, it will be saved, live, long live!

"The voucher is not what you want to bring, you can bring it with you"

By default, cross-domain requests do not provide credentials (cookies, HTTP authentication, SSL certificates, etc.), but by setting the withCredentials property of xhr to true, you can specify a request to send credentials. If the server accepts a request with credentials, it responds with an Access-Control-Allow-Credentials:true in the response header.

First we change the getData method in index.html:

function getData(){
var xhr = new XMLHttpRequest();
//允许跨域携带请求
   xhr.withCredentials = true;
   xhr.onreadystatechange = function () {
if(xhr.readyState == 4){
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
         console.log(xhr.responseText)
}else {
         console.log('err' + xhr.status);
}
}
};
   xhr.open('put','http://m.zhuanzhuan.com/getData',true);
   xhr.send(null);
}


我们刷新页面发送请求: 


Wishful thinking, meaning that if the response header of the preflight request is not set to true for Access-Control-Allow-Credentials, then the browser will not give the response to javascript, responseText is an empty string, status is 0. , trigger onerror(), the request to carry the credential across the domain is not allowed to drop.

We add the following code to the server.js response:

res.header("Access-Control-Allow-Credentials",'true');


再次请求,可以看到:


Dragging the house with a mouthful of Guandong success!

For requests with credentials, the server must not set the value of Access-Control-Allow-Origin to "*". The value must be the domain name specified in the Origin header field, which is the source of the attached credentials. For a look, server.js Slightly modified:

res.header("Access-Control-Allow-Origin", "*");


发送请求,可以看到: 


The motherland and the river are red, don't say you should understand it, you can't do it, NOOO!

"...I can't make it out... Anyway, it's about introducing the players on both sides =="

Finally, we will list some of the common fields of the Access-Control-Allow family in the response headers, and give our knowledge system a few solid blocks of 80 blocks:

HTTP response header field

  1. Access-Control-Allow-Origin: |* : Indicates the request source that can request data
  2. Access-Control-Expose-Headers:zhuanzhuanFe : In the cross-domain access, the getResponseHeader() method of the XMLHttpRequest object can only get some basic response headers, Cache-Control, Content-Language, Content-Type, Expires, Last- Modified, Pragma, if you want to access other headers, you need the server to set this response header, as above, the browser can access the zhuanzhuanFe response header through getResponseHeader.
  3. Access-Control-Allow-Credentials: true : Fields that allow credentials to be carried across domains
  4. Access-Control-Max-Age: < delta-seconds > : to specify the validity period of this preflight request, in seconds, during which the browser does not need to send a preflight request for the same request again. Note that the browser itself maintains a maximum valid time and will not take effect if the value of the header field exceeds the maximum valid time.
  5. Access-Control-Allow-Methods: < method >[, < method >] : The header field is used for the response of the preflight request. It indicates the HTTP method that is allowed for the actual request.
  6. Access-Control-Allow-Headers: < field-name >[, < field-name >]* : If the browser request includes an Access-Control-Request-Headers field, the Access-Control-Allow-Headers field is required. It is also a comma-separated string indicating all header fields supported by the server, not limited to the fields requested by the browser in "preflight".

HTTP request header field

  1. Origin: < origin > : indicates the source of the preflight request or the actual request
  2. Access-Control-Request-Method: < method > : Tells the server the HTTP method used by the actual request.
  3. Access-Control-Request-Headers: < field-name >[, < field-name >]* : Tell the server the header field carried by the actual request

Almost so much, thank you, some of the poor summary of the front-end small newcomers, the problematic place also hope that the predecessors have a lot of advice, work together in the grassland of the front end! Drive ~~~~