前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >跨域资源共享的使用

跨域资源共享的使用

作者头像
IMWeb前端团队
发布2017-12-29 14:48:44
1.4K0
发布2017-12-29 14:48:44
举报
文章被收录于专栏:IMWeb前端团队IMWeb前端团队

前言

页面中常常会有需要跨域通信的需求实现,我们知道浏览器的同源策略是不允许不同域之间的相互通信的(这里不深究域的定义及如何才算跨域),比如a.com有b.com想要的数据,那么在b.com页面中发送ajax请求到a.com是不允许的,相信大家都知道一些跨域通信的实现方法:

  • JSON-P(安全性不好)
  • window.name + iframe(实现的方式恶心)
  • window.postMessage(HTML5)
  • proxy(麻烦的部署及维护)
  • ...

跨域资源共享(Cross-Origin Resource Sharing)是W3C的一项规定,它规定了在浏览器中,基于XMLHttpRequest对象的跨域请求通信的原理,基本上保持了原有对象的用法。

CORS需要服务器端及客户端双方面的更改支持。本文主要介绍如何发起一个跨域请求和如何在服务器端支持CORS。

兼容性

  • Chrome 3+
  • Firefox 3.5+
  • Opera 12+
  • Safari 4+
  • Internet Explorer 8+

发起一个跨域请求

  1. 第一步新建XMLHttpRequest对象
代码语言:javascript
复制
function get_CORS_XHR(method, url) {
    var xhr = new XMLHttpRequest();
    if ("withCredentials" in xhr) {
        // "withCredentials"属性只存在于XMLHttpRequest2对象中
        // Chrome, Firefox, Opera and Safari
        xhr.open(method, url, true);

    } else if (typeof XDomainRequest != "undefined") {
        // XDomainRequest对象,兼容IE
        xhr = new XDomainRequest():
        xhr.open(method, url);

    } else {
        xhr = null;

    }
    return xhr;
}

var xhr = get_CORS_XHR('GET', 'http://example.com');
  1. 事件处理

XMLHttpRequest2对象新增了许多事件类型,原先的对象只支持onreadystatechange,新增事件有: (*星号代表IE不支持)

  • onloadstart*(请求开始发送)
  • onprogress(加载和发送数据中)
  • onabort*(实例abort方法被调用)
  • onerror(请求失败)
  • onload(请求成功)
  • ontimeout(请求超时)
  • onloadend(请求完成,不管失败还是结束)
代码语言:javascript
复制
// 通常我们处理最多的就是
xhr.onload = function() {
    var responseText = xhr.responseText;
    console.log(responseText);
    // ...
};

xhr.onerror = function() {...};
  1. withCredentials & 发送

默认情况下,标准的CORS请求是不会发送任何cookie信息的。如果你想让请求带上对应域的cookies信息(需要server支持),那么你需要:

代码语言:javascript
复制
xhr.withCredentials = true;
// 响应报文头部加上 Access-Control-Allow-Credentials: true

// handlers ...
xhr.send();

Server跨域请求处理支持

请求分类

可以给跨域请求分个类:

  1. 简单请求

符合下列要求的请求可以说是简单请求:

代码语言:javascript
复制
- HTTP Method
    - HEAD
    - GET
    - POST
- HTTP Headers
    - Accept
    - Accept-Language
    - Content-Language
    - Last-Event-ID
    - Content-Type
        - application/x-www-form-urlencoded
        - multipart/form-data
        - text/plain
  1. 不太简单的请求(A term by Monsur Hossain)

不符合(1)中的条件的请求

浏览器如Chrome, Firefox等会在不太简单的CORS请求发送前,为安全性考虑先发送一条”preflighted”OPTIONS请求

服务器端的处理根据请求的复杂程度处理方式有所不同。

处理简单请求

让我们举个栗子,CORS指定头部为粗体: Javascript:

代码语言:javascript
复制
var url = 'http://api.alice.com/cors';
var xhr = get_CORS_XHR('GET', url);
xhr.send();

HTTP 请求:

代码语言:javascript
复制
GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

值得注意的是CORS请求中必定包含Origin头部,但是包含此头部不一定意味着这个请求就是CORS请求。一个成功的跨域请求的响应报文可以是:

代码语言:javascript
复制
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8

所有CORS相关的头部均以Access-Control-开头,下面是关于各个头部的细节:

  • Access-Control-Allow-Origin(required) 此头部必须添加到响应报文中 ,不然缺省值会导致CORS请求失败。你可以设置*值让所有站点都可以访问你的数据,但最好还是控制一下
  • Access-Control-Allow-Credentials(optional) 设置此头部的值为true,如果你想要请求附带cookies。与上文提到的withCredentials属性协作。若此头部值为true而withCredentials属性为false,会导致请求失败,反之亦然
  • Access-Control-Allow-Expose-Headers(optional) XMLHttpRequest2对象存在getResponseHeader方法,允许访问一些简单的响应头部如:Content-Type,Cache-Control等等。如果想暴露一些特殊的头部,可以在此头部的值设置以逗号分隔的头部名称
处理不太简单的请求

如上文所说,处理不太简单的请求时,浏览器会先发出一次preflighted的请求,得到服务器允许后才执行真正的跨域请求,preflighted请求的结果会被缓存,多条请求同一服务器的跨域请求只会发送一次preflighted请求。举个栗子: Javascript:

代码语言:javascript
复制
var url = 'http://api.alice.com/cors';
var xhr = createCORSRequest('PUT', url);
xhr.setRequestHeader(
    'X-Custom-Header', 'value');
xhr.send();

Preflight请求:

代码语言:javascript
复制
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...
  • Access-Control-Request-Method 真实的请求方法,在此为PUT。所有的Preflight请求都应该包含此头部
  • Access-Control-Request-Headers 值是以逗号分隔的头部名称,代表请求附带的其余头部

Preflight响应:

代码语言:javascript
复制
Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
  • Access-Control-Allow-Origin(同上文)
  • Access-Control-Allow-Credentials(同上文)
  • Access-Control-Allow-Methods(required) 允许跨域请求的请求类型,以逗号分隔。由于preflight响应可能被缓存,所以此头部设置会有所帮助
  • Access-Control-Allow-Headers 当请求中有Access-Control-Request-Headers头部时,此响应头说明服务器支持的头部,以逗号分隔
  • Access-Control-Max-Age(required) 指定preflight响应可以被缓存的时间,单位秒

真实的请求跟响应就可以正常发送接收了。如果服务器对preflight请求直接返回HTTP 200,不包含任何CORS指定的头部,那么这个跨域请求就会失败,触发onerror事件。控制台中会输出类似一下的报错信息:

XMLHttpRequest cannot load http://api.alice.com. Origin http://api.bob.com is not allowed by Access-Control-Allow-Origin.

Server处理流程图

注: 最好在服务器端的Access-Control-Allow-Methods头部加上OPTIONS方法

代码语言:javascript
复制
Access-Control-Allow-Methods: GET,POST,OPTIONS
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: http://qunedu.oa.com
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 发起一个跨域请求
      • Server跨域请求处理支持
        • Server处理流程图
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档