本文已参与「新人创作礼」活动,一起开启创作之路。

跨域的现象

目前接触的大部分项目都是前、后端分离的项目,后端提供API服务,前端提供静态页面,调用后端的API接口。

当我们在本地启动前、后端代码进行调试时,如果使用postman等类似的工具请求后端API接口时,接口是可以请求的,但是通过浏览器发送同样的请求时会失败,这是为什么呢?

什么是跨域?

浏览器有一个安全机制叫同源策略。 同源就是指协议、域名、端口都一样,如果任意一项不一致就是不同源。简单点说就是,你的网页URL和你调用的接口URL不是一个地方的,浏览器觉得有安全风险,不想让你使用这个接口的数据,于是就会出现跨域请求被拦截的现象。

为什么postman不会出现跨域?

这是因为postman 发送请求都是独立请求一个资源 ,而不是在一个网站返回的页面里,再去请求另外一个网站 / 端口的资源。自然也就不会造成跨域

【go商城】gin+vue跨域问题

而当我们在前端项目中模拟请求时,就会出现跨域导致接口请求失败

【go商城】gin+vue跨域问题

gin处理跨域

我们在gin的拦截器中定义允许跨域请求,同时需要在注册url的时候使用这个拦截器的方法

Router.Use(middleware.Cors()) // 如需跨域可以打开

在跨域前,浏览器会首先发出options的请求,这种请求需要直接返回200通过后浏览器才会真正的发送跨域的请求。

// 处理跨域请求,支持options访问
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin")
		c.Header("Access-Control-Allow-Origin", origin)
		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id")
		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")
		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusNoContent)
		}
		// 处理请求
		c.Next()
	}
}

到这里就结束了嘛?

如果你的拦截器方法中定义了你前段请求携带的所有信息的话,那么跨域请求就能成功。

但是我使用的newbee商城现成的前端,其前端代码中携带了这样一条请求头的键值对

axios.defaults.headers[‘X-Requested-With’] = ‘XMLHttpRequest’

【go商城】gin+vue跨域问题
如果不在后端的拦截器加上这个请求头的话,通过浏览器请求接口依然会失败。所以需要在拦截器中允许这个请求头的通过

c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id,X-Requested-With")

在你的项目中如果也遇到了类似的问题,可以检查一下你前后端定义的请求头是否一致。

结语

除了上述直接在后端代码的拦截器中设置跨域请求,生产环境中通常会通过nginx的相关配置对跨域请求进行配置。

本文所有代码已上传github,有兴趣的可以访问github.com/newbee-ltd/… 如果有更好的建议或者发现了BUG欢迎提交issure,pr我将尽快处理