跨域请求是指浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任意一个不同就会触发跨域限制,这是浏览器的同源策略导致的。在Golang开发的HTTP服务中,我们可以通过配置响应头的方式支持跨域请求。

不使用第三方框架实现跨域
如果使用Golang标准库的net/http开发HTTP服务,我们可以自定义一个中间件来处理跨域相关的响应头。首先定义中间件函数,在中间件中给每个响应添加对应的跨域头信息。
package main
import (
"net/http"
)
// 跨域中间件
func corsMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(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")
// 预检请求缓存时间,单位秒
w.Header().Set("Access-Control-Max-Age", "86400")
// 处理OPTIONS预检请求,直接返回成功
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusNoContent)
return
}
next.ServeHTTP(w, r)
})
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello golang cors"))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", helloHandler)
// 使用跨域中间件包装路由
handler := corsMiddleware(mux)
http.ListenAndServe(":8080", handler)
}
跨域响应头参数说明
- Access-Control-Allow-Origin:指定允许访问该资源的外部请求源,*表示允许所有源,生产环境建议设置为具体的域名,比如https://ipipp.com。
- Access-Control-Allow-Methods:指定允许的HTTP请求方法,多个方法用逗号分隔。
- Access-Control-Allow-Headers:指定允许的请求头字段,前端自定义的头需要在这里声明。
- Access-Control-Allow-Credentials:设置为true时允许请求携带Cookie等凭证,此时Access-Control-Allow-Origin不能设置为*。
- Access-Control-Max-Age:预检请求的缓存时间,在缓存时间内浏览器不会再发送预检请求。
使用gin框架实现跨域
gin是Golang中常用的Web框架,实现跨域的方式更加简洁,我们可以自定义中间件或者使用第三方库。这里先介绍自定义中间件的实现方式,逻辑和标准库的实现类似。
package main
import (
"github.com/gin-gonic/gin"
)
// gin跨域中间件
func cors() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
c.Header("Access-Control-Allow-Credentials", "true")
c.Header("Access-Control-Max-Age", "86400")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func main() {
r := gin.Default()
// 注册跨域中间件
r.Use(cors())
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello gin cors",
})
})
r.Run(":8080")
}
如果不想自己实现中间件,也可以使用gin的第三方CORS库,首先通过命令安装依赖:
go get github.com/gin-contrib/cors
然后使用库提供的配置方式实现跨域,这种方式可以更灵活地配置允许的源、方法等参数。
package main
import (
"time"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// 配置跨域
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"https://ipipp.com"}, // 允许的源
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "hello gin cors with lib",
})
})
r.Run(":8080")
}
跨域配置注意事项
不要在生产环境随意将Access-Control-Allow-Origin设置为*,这会引入安全风险,建议只允许可信的域名访问接口。如果接口需要携带Cookie等凭证,Access-Control-Allow-Origin必须设置为具体的域名,不能使用通配符。
另外,预检请求是浏览器自动发送的OPTIONS请求,不需要前端额外处理,后端只需要正确响应即可。如果配置的跨域头不生效,可以检查响应头是否正确返回,或者是否有其他中间件覆盖了跨域配置。