请求头
浏览器会自动帮你先发出一个叫做预检(cors-preflight-request)的请求, 对应的 HTTP Request Method 为 OPTIONS。这个请求对服务器是安全的,也就是说不会对服务器的资源做任何改变,仅仅用于确认 header 响应
。
该请求 header 中会包含以下两个字段:
Access-Control-Request-Method
: 该字段的值对应当前请求类型,例如 GET、POST、PUT等等。浏览器会自动处理。Access-Control-Request-Headers
: 该字段的值对应当前请求可能会携带的额外的自定义 header 字段名,多个字段用逗号分割。浏览器会自动处理,将请求中非简单的 header 字段全部列出来,例如标识请求流水的x-request-id
,用于 Auth 鉴权的Authorization
字段。
对于 OPTIONS 请求,按照规范实现的服务端会响应一组HTTP header,但不会返回任何实体内容。如果服务端支持该跨域请求,建议返回 204 状态码(返回 200 也可以)。如果不支持,建议返回 403 状态码(返回 404 或其他错误状态码也可以)。
响应的 header 可以包含以下字段:
Access-Control-Allow-Origin
: 允许哪些域被允许跨域,例如http://qq.com
或https://qq.com
,或者设置为*
,即允许所有域访问(通常见于 CDN )Access-Control-Allow-Credentials
: 是否携带票据访问(对应 fetch 方法中credentials
),当该值为 true 时,Access-Control-Allow-Origin
不允许设置为*
Access-Control-Allow-Methods
: 标识该资源支持哪些方法,例如:POST, GET, PUT, DELETEAccess-Control-Allow-Headers
: 标识允许哪些额外的自定义 header 字段和非简单值的字段(这个后面会解释)Access-Control-Max-Age
: 表示可以缓存Access-Control-Allow-Methods
和Access-Control-Allow-Headers
提供的信息多长时间,单位秒,一般为10分钟。Access-Control-Expose-Headers
: 通过该字段指出哪些额外的 header 可以被支持。
发送流程
当我们发起跨域请求时,如果是非简单请求,浏览器会帮我们自动触发预检请求,也就是 OPTIONS 请求,用于确认目标资源是否支持跨域。如果是简单请求,则不会触发预检,直接发出正常请求。
浏览器会根据服务端响应的 header 自动处理剩余的请求,如果响应支持跨域,则继续发出正常请求,如果不支持,则在控制台显示错误。
简单请求
请求方法必须是以下之一:GET、HEAD、POST,也就是说 PUT、PATCH 等方法必然会触发预检。
只有以下 header 字段允许被修改或被设置,否则必然触发预检。
Accept
、Accept-Language
、Content-language
、Content-Type
(但有限定值)、DPR
、Downlink
、Save-Data
、Viewport-Width
、Width
Content-Type
的值只被允许设置为以下三个之一:application_x-www-form-urlencoded
、multipart_form-data
、text/plain
。也就是说,如果请求的Content-Type
被设置为application/json;charset=utf-8
时也必然会触发预检。- 添加任何额外的自定义的 header 都会触发预检,例如 x-request-id,但服务端可以设置缓存这一个请求的OPTIONS 响应。
XMLHttpRequestUpload 在请求中使用的任何对象上都没有注册事件侦听器。这个比较少见。
ReadableStream
请求中未使用任何对象。这个比较少见,应该是指 Fetch API 中的 Request 中的 Body,本人没有去验证。
也就是说带有 Auth 鉴权的 Authorization
字段也非简单请求。
CDN引用
<script src="https://qq.com/a.js" crossOrigin="anonymous"></script>
CDN 设置了 Access-Control-Allow-Origin
响应头允许跨域时,我们可以给script
标签添加crossOrigin
属性,从而可以使用 window.onerror
捕获 CDN 上的 js
运行时导致的详细错误信息,包括堆栈等。
如果不设置crossOrigin
属性,则可能只会捕获到script error
,无法获取额外的堆栈信息。