不久前在公司写了一个基于 Hapijs 的后端项目,感受这个框架颇有本身的特色,跟 Express 和 Koa 的区别比较大,体现了配置大于编码的思想。用起来很方便,听说 Walmart 团队用这个框架扛住了黑五的流量,看起来在实际项目中也有可用性,推荐你们尝试一下~html
有点跑题了,这篇文章主要写我在开发过程当中所遇到的一个问题,以及我从这个问题所学习到的东西,而后我是怎么解决这个问题的。前端
个人项目需求是写一个 App 版本管理器,先后端都由我开发。前端分为两个部分:运营人员写版本更新说明的内部系统和 App 访问的产品页;后端就是对 App 版本进行管理的 CURD 接口。重点在于三个部分的程序部署在三台服务器上,前端的两个系统在不一样的服务器对第三个服务器上的接口进行数据请求,这就不可避免的涉及到了跨域。程序员
固然,只是跨域的话也不难解决,添加 Access-Control-Allow-Origin
为要跨域的域名就 OK 了,或者直接赋值为 *
。可是个人部分接口涉及鉴权,经过 JWT 进行校验,若是 JWT 不合法,那么会返回 401 Unauthorized 错误;而个人 JWT 是经过请求头的自定义字段 authorization
带到服务器的,这就致使一个更加麻烦的问题出现了 —— 预检请求。segmentfault
预检请求(preflight request),是一个跨域请求,用来校验当前跨域请求可否被理解。后端
它使用 HTTP 的 OPTIONS 请求,通常会包括一下请求头:Access-Control-Request-Method
,Access-Control-Request-Headers
和 Origin
。api
预检请求一般在必要的时候由浏览器自动发起,不须要程序员进行干预。跨域
若是咱们想要知道服务器是否支持一个 DELETE
请求,在发送 DELETE
请求以前,服务器一般会发送一个以下的预检请求:浏览器
OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org
复制代码
若是服务器容许使用 DELETE
方法的话,会返回以下响应头;其中 Access-Control-Allow-Methods
会列出 DELETE
方法,表明服务器支持这个方法。bash
HTTP/1.1 200 OK
Content-Length: 0
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
复制代码
以上资料来源于 MDN服务器
由此可知,预检请求是一个用于校验服务器是否支持当前方法以及是否可以理解当前请求的一种请求,它区别于通常的请求,不禁代码发起,而在必要的时候由浏览器自动发出。
因此这里就出问题了,若是咱们不知道何时浏览器会发出预检请求,那么服务器没有作处理的话就会致使 CORS 报错的出现。
接下来再深刻一点。
知足如下条件的请求就是简单请求:
1、请求方法属于下面三种方法之一:
2、HTTP 的请求头信息超出一下范围:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:超出这三个的范围:
不知足以上条件的请求就是非简单请求。
若是是简单的 CORS 请求,浏览器会自动在请求头中添加一个 Origin 请求头字段,若是响应头对应的 Access-Control-Allow-Origin
没有包含 Origin 所指定的域,那么就会报 CORS 错误,请求失败。因此服务器的响应要添加对应的响应头。
若是是非简单的 CORS 请求,那么会有一次预检请求,在正是请求以前发出一个 OPTIONS 请求对服务器进行检测。
除了有 Origin 之外,预检请求的请求头还包括一下两个特殊字段:
Access-Control-Request-Method
:表示 CORS 请求要用到的请求方法。
Access-Control-Request-Headers
:这是一个用逗号分割的字符串,指出 CORS 请求要附加的请求头。
服务器的响应能够包含如下字段:
Access-Control-Allow-Methods
:逗号分割的字符串,表示容许的跨域请求方法。
好比:
Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS
复制代码
Access-Control-Allow-Headers
:若是浏览器请求包含 Access-Control-Request-Headers
字段,那么服务器中该响应头也是必须的,也是一个由逗号分隔的字符串,表示服务器支持的请求头。
好比:
Access-Control-Allow-Headers: authorization
复制代码
Access-Control-Max-Age
:可选字段,设置当前预检请求的有效期,单位为秒。
Access-Control-Allow-Credentials
:可选字段。默认状况下,CORS 请求不携带 cookie,若是服务器想要 cookie,须要指定该请求头为 true
。
避免出现预检请求,须要使得你的请求知足简单请求的两个条件。
好比在使用 JWT 鉴权时,可能会把你的 token 放在请求头的 authorization 字段,由于这个字段超出了简单请求的范围,因此请求会变成非简单请求。这时能够不把 token 放在 authorization 请求头中。
出现预检请求后,进行服务器配置,分别设置好 Access-Control-Allow-Origin
、Access-Control-Allow-Methods
和 Access-Control-Allow-Headers
,使得你的非简单请求可以经过预检请求。
若是使用 Hapijs 的话,只须要在路由配置中增长 cors: true
配置便可。