Axios Gin跨域

528 阅读3分钟

前端使用axios,后端使用go-gin框架开发

请看下列代码

// 在http.js中引入axios
import Vue from "vue";
import axios from "axios"; // 引入axios

axios.defaults.timeout = 15000;
axios.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8";
axios.defaults.headers.post["Access-Control-Allow-Origin"] = "*";
func Cors() gin.HandlerFunc {
   return func(c *gin.Context) {
      method := c.Request.Method
      origin := c.Request.Header.Get("Origin")
      if origin != "" {
         c.Header("Access-Control-Allow-Origin", "*")  
         c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
         c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization")
         c.Header("Access-Control-Allow-Credentials", "true")
         c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") 
      }
      if method == "OPTIONS" {
         c.AbortWithStatus(http.StatusNoContent)
      }
      c.Next()
   }
}

好的,前后端均做跨域处理,但是却依旧请求失败

微信截图_20221018143328.png

跨域回顾

浏览器的请求分为,简单请求,非简单请求。简单请求浏览器不会预检,而非简单请求会预检。

简单请求

同时满足下列三大条件,就属于简单请求,否则属于非简单请求

请求方式只能是:GET、POST、HEAD HTTP请求头限制这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID

Content-type只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单请求,浏览器直接请求,会在请求头信息中,增加一个origin字段,来说明本次请求来自哪个源(协议+域名+端口)。服务器根据这个值,来决定是否同意该请求。服务器返回的响应会多几个和跨域相关的头信息字段

  • Access-Control-Allow-Origin:该字段是必须的,* 表示接受任意域名的请求,还可以指定域名
  • Access-Control-Allow-Credentials:该字段可选,是个布尔值,表示是否可以携带cookie,(注意:如果Access-Control-Allow-Origin字段设置*,此字段设为true无效)
  • Access-Control-Allow-Headers:表明服务器允许请求中携带字段 ,如Cache-Control、Content-Type、Expires等
  • Access-Control-Max-Age:有效时间,在有效时间内,浏览器无须为同一请求再次发起预检请求

非简单请求

非简单请求是对那种对服务器有特殊要求的请求,比如请求方式是PUT或者DELETE,或者Content-Type字段类型是application/json。都会在正式通信之前,增加一次HTTP请求,称之为预检。

浏览器会先询问服务器,当前网页所在域名是否在服务器的许可名单之中,服务器允许之后,浏览器会发出正式的XMLHttpRequest请求,否则会报错。(备注:之前碰到预检请求后端没有通过,就不会发正式请求)

axios 的处理

经过不断尝试,发现在axios代码中如果去掉

axios.defaults.headers.post["Access-Control-Allow-Origin"] = "*"; 这行代码的话,请求可以了。但这样并不满足我们的需要,倘若前端不是我们自己开发的,后端如何处理才能解决这种问题呢?

问题解决

仔细思考后端代码逻辑,做如下修改:

func Cors() gin.HandlerFunc {
   return func(c *gin.Context) {
      method := c.Request.Method
      origin := c.Request.Header.Get("Origin")
      if origin != "" {
         c.Header("Access-Control-Allow-Origin", "*")  
         c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
         c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization")
         c.Header("Access-Control-Allow-Credentials", "true")
         c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,Cache-Control,Content-Language,Content-Type,Expires,Last-Modified,Pragma,FooBar") 
      }
      if method == "OPTIONS" {
          /* 添加 */
         c.Header("Access-Control-Allow-Origin", "*")  
         c.Header("Access-Control-Allow-Methods", "OPTIONS")
         c.Header("Access-Control-Allow-Headers", "*")
          /* 添加 */
         c.AbortWithStatus(http.StatusNoContent)
      }
      c.Next()
   }
}

在请求是OPTIONS的时候,需要对请求头进行重新赋值,经过测试 c.Header("Access-Control-Allow-Headers", "*") 问题出现在这里,必须填* 否则都会出现上述问题

猜测可能axios或者浏览器内部的bug,前端不设置"Access-Control-Allow-Origin"就没有问题,或者前端设置,但是后端不对"Access-Control-Allow-Origin"做处理,具体不知道他们的交互逻辑是什么,也有人提出类似的问题,但是都没有说明原因,笔者这里也仅仅是猜测。