跨域预检请求是浏览器在发送某些复杂跨域请求前,先向目标服务器发送的OPTIONS类型请求,用于确认服务器是否允许当前跨域请求。在Golang开发中,如果后端接口没有正确响应预检请求,前端就会收到跨域错误,导致接口调用失败。

跨域预检请求的触发条件
并非所有跨域请求都会触发预检,只有满足以下任一条件的跨域请求才会先发送预检请求:
- 请求方法不是GET、POST、HEAD这三种简单方法
- 请求头包含自定义字段,或者Content-Type的值不是application/x-www-form-urlencoded、multipart/form-data、text/plain
- 请求中使用了ReadableStream等对象
手动处理预检请求的实现方式
我们可以在Golang的接口处理逻辑中,先判断请求方法是否为OPTIONS,如果是则直接返回对应的CORS响应头,不需要执行业务逻辑。
基础实现示例
以下是一个简单的HTTP接口处理预检请求的示例代码:
package main
import (
"net/http"
)
func corsMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 设置允许的源,生产环境建议指定具体域名
w.Header().Set("Access-Control-Allow-Origin", "*")
// 设置允许的请求方法
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
// 设置允许的请求头
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
// 设置是否允许携带凭证
w.Header().Set("Access-Control-Allow-Credentials", "true")
// 如果是预检请求,直接返回成功状态
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
// 执行后续的业务处理逻辑
next(w, r)
}
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
}
func main() {
http.HandleFunc("/hello", corsMiddleware(helloHandler))
http.ListenAndServe(":8080", nil)
}
代码逻辑说明
上述代码中我们定义了corsMiddleware中间件,所有需要支持跨域的接口都先经过这个中间件处理。中间件首先设置CORS相关的响应头,然后判断请求方法是否为OPTIONS,如果是则直接返回204状态码,不需要执行后续的业务处理函数。这样前端发送的预检请求就能得到正确响应,后续的真实请求就可以正常发送了。
使用第三方库简化处理
如果项目中需要处理更复杂的CORS场景,手动编写中间件会比较繁琐,我们可以使用成熟的第三方库github.com/rs/cors来简化操作。
安装依赖
首先执行以下命令安装库:
go get github.com/rs/cors
使用示例
使用该库处理跨域预检请求的代码如下:
package main
import (
"net/http"
"github.com/rs/cors"
)
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello world"))
}
func main() {
// 配置CORS规则
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, // 允许的源
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, // 允许的方法
AllowedHeaders: []string{"Content-Type", "Authorization"}, // 允许的请求头
AllowCredentials: true, // 是否允许凭证
Debug: false, // 是否开启调试日志
})
// 将处理器包装为支持CORS的处理器
handler := c.Handler(http.HandlerFunc(helloHandler))
http.Handle("/hello", handler)
http.ListenAndServe(":8080", nil)
}
这个库会自动处理预检请求和正常请求的CORS响应头设置,不需要我们手动判断请求方法,大大减少了重复代码的编写。
注意事项
- 生产环境中不建议将
Access-Control-Allow-Origin设置为*,应该指定具体的前端域名,避免安全风险 - 如果接口需要支持携带Cookie等凭证,除了设置
Access-Control-Allow-Credentials为true,Access-Control-Allow-Origin也不能设置为* - 预检请求的缓存时间可以通过
Access-Control-Max-Age响应头设置,单位是秒,设置后浏览器会在缓存时间内不再重复发送预检请求,减少请求次数