何谓http 非“简单标头”?
在我们从前端发送网络请求时所携带的标头非这些标头“Accept, Accept-Language, Content-Language, Content-Type, Range” 时即“非简单标头”。
预检请求
当我们发送的请求头包含的请求头不是CORS 白名单的请求标头 时浏览器就会在请求发起之前自动发送一个预检请求。 只有在预检请求的结果OK后才会发起真正的请求。
预检请求官方文档https://developer.mozilla.org/zh-CN/docs/Glossary/Preflight_request
vue axios中何时会触发预检请求?
结果试验,在使用axios框架的情况下, 只要是非get请求, 且使用axios中的 data 发送了请求的数据, 浏览器就会认为是非简单请求, 就会在真正请求发起之前自动发送一个预检请求来确定服务器是否支持相应的请求。
vue axios中的请求
解决方法
目前网上大部分的所谓的解决方案是使用一个 叫qs框架(这个东西的作用就是将我们的data请求数据转换为url参数, 从而将POST data请求转换为简单请求), 其实这样做是非常不靠谱的方式,首先在请求参数较多时请求直接无法正常运行, 其次请求参数每次请求执行都会进行二次转换费时费力,白白浪费系统资源,还有降低了网络请求数据的私密性,有数据安全风险等。
正确的解决方法
在服务端针对前端浏览器发送的预检请求(即请求方式为 OPTIONS 的请求)做出相应的回应,
例如,客户端可能会在实际发送 DELETE 请求之前,先向服务器发起预检请求,用于询问是否可以向服务器发起 DELETE
请求:
HTTP
OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org
如果服务器允许,那么服务器就会响应这个预检请求。并且其响应标头 Access-Control-Allow-Methods 会将 DELETE
包含在其中:
HTTP
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
gin框架中的预检请求回应示例
在gin框架中,我们可以有2种方式对预检请求进行回应:
1. 针对每个非GET的路由都增加一个OPTIONS的路由, 将他们统一指向一个专门来处理预检请求的函数或者方法。
2. 使用gin路由中的 r.NoRoute方法将所有的非路由请求都指向我们设定的专门处理预检请求的函数或者方法中, 然后再这个方法中通过请求方式 OPTIONS来统一处理预检请求, 非OPTIONS请求则当做404请求处理。
// 统一处理预检请求 和没有路由的请求
func NoRouteHandler(c *ginx.XContext) {
origin := c.Request.Header.Get("Origin")
// 获取当前请求方式
method := c.Request.Method
// 如是预检请求直接终止当前请求并返回状态为ok
if method == "OPTIONS" {
// 这个是响应给预检请求的头信息,这个必须包含Access-Control-Allow-Origin 否则CORS不通过!!!
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
// 预检允许的请求头信息, 注意必须包含
c.Writer.Header().Set("Access-Control-Allow-Headers",“Accept, Accept-Language, Content-Language, Content-Type, Range, origin, Authorization, Token, X-Token, AccessToken”)
// CORS预检响应缓存 预检响应缓存时间 86400 秒 即1天
c.Writer.Header().Set("Access-Control-Max-Age", "86400")
// 退出gin请求链路并返回OK状态
c.AbortWithStatus(http.StatusOK) // 这个很关键 告诉浏览器预检成功了
return
}
// 其他情况处理。。。。。。
}