server-请求预检跨域问题

server-请求预检跨域问题


前篇

前端业务发起一次跨域的 http 请求时, 如果设置有请求头如 Authorization 等校验的数据, 例如

1
2
var xhr = new XMLHttpRequest();
xhr.setRequestHeader("Authorization", "aaa")

浏览器实则会先发起两次请求, 一次时预检 (preflight) 请求 (OPTIONS 方法), 一次是业务正常的请求. 预检成功 (一般是返回 204 状态码) 时, 才会进行业务请求

image-20240319161029999

服务器要对 预检 请求进行相应回应, 一般回应状态码 204, 不然会报错预检跨域失败

1
Access to XMLHttpRequest at 'http://192.168.1.28:6353/recv/msg' from origin 'http://192.168.1.28:57090' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

实测

以 golang 的 gin 框架为例

有两种解决办法

  1. 对每个 handler 进行 OPTIONS 判断, 如果有则返回状态码 204, 推荐这种方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    func handlerCors() gin.HandlerFunc {
    return func(c *gin.Context) {
    c.Header("Access-Control-Allow-Origin", "*")
    c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
    c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, User-Token, User-Id, User-Uid")
    c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
    c.Header("Access-Control-Allow-Credentials", "false")
    c.Header("Access-Control-Max-Age", "86400")

    if c.Request.Method == "OPTIONS" { // 响应 204
    c.AbortWithStatus(http.StatusNoContent)
    }
    c.Next()
    }
    }

    router := gin.Default()
    router.Use(handlerCors())
  2. 使用另一个框架 cros: go get github.com/gin-contrib/cors

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import "github.com/gin-contrib/cors"

    router := gin.Default()
    // 允许跨域请求
    cfg := cors.DefaultConfig()
    cfg.AddAllowHeaders("Authorization")
    cfg.AllowCredentials = true
    cfg.AllowAllOrigins = false
    cfg.AllowOriginFunc = func(origin string) bool { return true } // 跨域检验
    router.Use(cors.New(cfg))

踩坑

未设置跨域

  • 报错: Access to XMLHttpRequest at 'https://xxx' from origin 'https://ccc' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

  • 可能得原因请求的 Content-Type 设置错误

    1
    2
    3
    4
    5
    var xhr = new XMLHttpRequest();

    //xhr.setRequestHeader('Content-Type', 'application/json;charset=utf-8')
    // 修改为
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");