在前后端分离的开发方式中,前端会提供一个URL
(统一资源定位符)供用户访问,前端运行中会调用后台api
接口的URL
进行数据操作,这两个URL
都包含以下部分:
http、tcp/ip
localhost(127.0.0.1)
,或者其他的ip
地址8080
当页面发送一个新的请求,该请求中的URL
与原页面的URL中的通信协议、主机号、端口号中任意一个有不同,就称为跨域访问。
举个例子,比如当前前端运行的页面的URL
是 http://localhost:8080
,在请求gin开发的后端时,该服务监听端口是http://localhost:8090
或者http://GinProject:8080
时,由于端口或者主机号不同,便发生了跨域访问。
在发生跨域的时候,浏览器和服务器会进行几个操作:
OPTIONS
类型的请求,即测试类型,又称为OPTIONS嗅探,同时会在Header
中带上一个origin
,后端跨域通过判断origin
来判断是否同意跨域请求。Access-Control-Allow-Origin
的值,通过与浏览器的origin
值进行匹配,如果匹配成功则代表有跨域权限,允许跨域。http
请求origin
部分跨域请求处理,
func Cors()gin.HandlerFunc{
return func(c *gin.Context){
origin := c.Request.Header.Get("origin") //origin值获取
if origin != ""{
//动态设置origin匹配,即同意所有跨域
c.Header("Access-Controk-Allow-Origin",origin)
//也是允许访问所有域,不过设置为'*'的时候不允许XMLHttpRequest携带Cookie
c.Header("Access-Controk-Allow-Origin","*")
//允许部分域,''中填写域名
c.Header("Access-Controk-Allow-Origin",'')
}
}
}
在非简单请求,即PUT/DELETE
以及字段类型Content-Type
为application/json
的请求中,会发送一个预检请求,即OPTIONS
嗅探请求,也就是后端的CORS
请求。
我们需要配置以下设置,来判断是否允许跨域请求
1.可访问域名
c.Header("Acces-Control-Allow-Origin",'*')
//必须字段。
//他的值是'*'或者origin的动态值,表示允许访问所有域
//或者设置的固定域名,表示访问对应域
2.可访问方法
c.Header("Access-Control-Allow-Methods",'PUT,DELETE,GET,POST')
//必须字段。
//值为用逗号分隔的字符串,该字符串中包含多个请求方法。
//其中的请求是所支持的请求,并且不能设置为动态的当前请求方法,因为会造成多次预检请求
3.支持的头信息字段
c.Header("Access-Control-Allow-Headers",'Content-Type, Content-Length, Token')
//如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。
//表示服务器支持的所有头信息字段
4.可被返回前端得到的字段
c.Header("Access-Control-Expose-Headers",'Access-Control-Allow-Headers, Token')
//可选字段
//表示XMLHttpRequest对象可获取的额外字段(不包含Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma)
5.是否可发送Cookie
c.Header("Access-Control-Allow-Credentials",'true')
//可选字段,如果不设置为true则不需要设置
//表示是否可以发送Cookie
6.预检有效期
c.Header("Access-Control-Max-Age","172800")
//可选字段
//表示该次OPTIONS请求有效期限,在此期间不要在此发送新的OPTIONS
中间件内容
package Mid
import (
"github.com/gin-gonic/gin"
"net/http"
)
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", origin)
c.Header("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Token")
c.Header("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token")
c.Header("Access-Control-Max-Age", "172800")
c.Header("Access-Control-Allow-Credentials", "true")
c.Set("content-type", "application/json") 设置返回格式是json
}
if method == "OPTIONS" {
//c.JSON(200, Controller.R(200, nil, "Options Request"))
c.AbortWithStatus(http.StatusNoContent)
}
c.Next()
}
}
使用
var r *gin.Engine
func init()*gin.Engine{
r = gin.Default()
r.Use(Mid.Cors())
r.POST("/")
return r
}
参考文章:
https://juejin.cn/post/6987205879653204004
https://juejin.cn/post/7013611832543805454