RESTful API 实践中的干货:CORS 跨域访问

什么是 CORS 跨域访问?

CORS(Cross-Origin Resource Sharing)是指跨域资源共享,在前端开发中经常会遇到的问题。简单来说,现代浏览器为了保护用户的浏览器安全,限制了浏览器从一个域名下发送 XMLHttpRequest 的请求时间。

举个例子:我们有个域名为 www.example.com,如果想从这个域名下的 HTML 页面通过 Ajax 请求获取另一个域名,比如 www.example.org 的数据,就会面对浏览器的阻拦。

解决方案

JSONP

在早期的前端开发中,为了解决跨域问题,存在一种叫做 JSONP(JSON with Padding)的技术。这种技术主要利用了 <script> 标签可以跨域的特性,将请求将转化成类似于动态创建一个 script 的方式进行加载。

但是,JSONP 只适用于 GET 请求,而且只能够支持 HTTP 的一些简单的操作。所以,现在我们通常使用的解决方案是 CORS。

CORS

CORS 技术需要依赖服务器端的配置。我们需要让服务器端允许跨域。

简单请求

简单请求是指满足下列需求之一的请求:

  1. 请求方法为 GET、HEAD 或者 POST。
  2. HTTP头信息不超过以下几种格式(区分大小写):
    1. Accept
    2. Accept-Language
    3. Content-Language
    4. Content-Type (只限于 application/x-www-form-urlencodedmultipart/form-datatext/plain

如果满足上述条件,那么浏览器将会多下发一个 OPTIONS 请求,用来获取服务器端的允许信息,再发起正式的请求。

在服务器端,只需要加入返回头信息 Access-Control-Allow-Origin,允许指定域名进行跨域访问就可以了。例如:

Access-Control-Allow-Origin: *

如果你想让访问来源都经过验证,可以这样做:

Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept

如果你只想允许某个域名进行访问,可以这样写:

Access-Control-Allow-Origin: http:// specificdomain.com

这里需要指出的是,针对 cookies,我们还需要指定 withCredentials 字段为 true,并且在服务器端的返回头信息需要加入 Access-Control-Allow-Credentials: true

非简单请求

如果简单请求的条件都不满足,就需要使用非简单请求的方法。非简单请求时浏览器的预检请求机制,同样需要服务器端进行配置。

具体的做法是服务器端需要在响应中加入以下响应头信息:

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

这样的是为了告诉浏览器,本站支持的请求方法。同时我们需要在响应头信息的 Access-Control-Allow-Headers 字段进行设置,这样,浏览器才会响应 POST 请求。

Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept

如果想要支持 cookies,则必须在响应头信息中加入下面这个字段:

Access-Control-Allow-Credentials: true

总结

以上就是跨域访问的基本介绍了,CORS 技术可以解决前端开发中,很多时候需要进行跨域访问的问题,不过需要注意的是,每次请求都会带上 Origin 字段,包含了请求来源的信息。简单请求需要加上 Access-Control-Allow-Origin 表明访问来源。尤其注意,如果针对 cookies,我们还需要指定 withCredentials 为 true。

示例代码

以下是一个简单请求的实例代码:

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
  if(xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);
  }
};
xhr.open('GET', 'http://domain-b.com/resource', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send();

// 响应头信息
Access-Control-Allow-Origin: *

以下是一个非简单请求的实例代码:

var preflightXHR = new XMLHttpRequest();
preflightXHR.open('OPTIONS', 'http://domain-b.com/resource', true);
preflightXHR.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
preflightXHR.setRequestHeader('X-Custom-Header', 'valueone');
preflightXHR.onreadystatechange = function() {
  if (preflightXHR.readyState === 4 && preflightXHR.status === 200) {
    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function() {
      if(xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText);
      }
    };
    xhr.open('POST', 'http://domain-b.com/resource', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
    xhr.setRequestHeader('X-Custom-Header', 'valueone');
    xhr.send('a=1&b=2');

    // 响应头信息
    Access-Control-Allow-Methods: GET, POST, PUT, DELETE
    Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept
    Access-Control-Allow-Credentials: true
  }
};
preflightXHR.send();

// 响应头信息
Access-Control-Allow-Origin: http:// http://domain-a.com

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b230bcadd4f0e0ffb5e256