关于跨域问题,网上的解释说明很多,比如:关于跨域,你想知道的全在这里 。纸上得来终觉浅,绝知此事要躬行,看的似懂非懂,所以这里结合自己遇到的问题场景,进行说明记录,加深理解。
1、问题场景和调试
在前端测试过程中,如下图所示登陆界面,点击 “登陆”按钮的时候,调用axios post请求,将用户填写的用户名、密码信息,发送给后端进行校验。
浏览器控制台提示错误 Access to XMLHttpRequest at '*' from origin '*' 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. 结合应用场景,就是前端http://192.168.31.190:8080向后端http://192.168.31.151:9090/api/v1/login请求数据的时候失败了。经查询,这就是传说中的“跨域“问题了!!!
既然发生错误,先看看问题出现在哪里,请求有没有成功发送?后端接口有没有接收到请求返回数据?为此,进行了一下测试,在不修改任何代码的情况下,在后端接口断点调试,前端再次发送请求,看后端能否接收到请求。测试结果发现,后端接口是可以收到请求且正常返回,如下图所示。
浏览器端切换到Network,看到的结果如下图所示,Failed to load response data。说明,请求已经发送出去了,后端也已经接收到请求并返回结果了,只是response数据没有被浏览器加载。这基本上就弄清楚了问题出现的点是在于浏览器。
那么关于浏览器为什么会这么做,简单来说就是浏览器同源政策,关于这部分,网上有很多大佬做了详细说明和举例,此处就不再赘述,具体可以查看浏览器同源政策及其规避方法。
2、问题解决方法
下面就来说说有什么解决方法。关于解决方法,前端常见跨域解决方案(全)、10 种跨域解决方案(附终极方案)这些文章也给出了足够多和专业的方法。我在这里也只想说,从我的理解上看,跨域问题的解决可以从前端解决也可以从后端解决。
(1)后端解决
后端解决跨域问题,从实现上看就比较简单,例如我使用的springboot搭建后端,此时仅需要在Controller类上添加一个“@CrossOrigin“注解就可以实现对当前Controller 的跨域访问了,代码如下所示。
@Api(value = "LoginController", tags = "登录界面")
@RestController
@RequestMapping("/api/v1")
@CrossOrigin
@Slf4j
public class LoginController {
@Autowired
private UserInfoMapper userInfoMapper;
@PostMapping("/login")
public CommonResult checkUserInfo(@RequestBody UserInfo userInfo){
List<UserInfo> userInfos = userInfoMapper.selectByUserNameAndPassword(userInfo.getUserName(), userInfo.getPassword());
if(userInfos!=null && userInfos.size()>0){
return CommonResult.success("登录成功");
}
return CommonResult.failed("用户不存在");
}
}
此时,再次点击界面登陆,浏览器网络请求和响应如下所示,浏览器成功加载response数据。
采用后端解决方式,Request URL为http://192.168.31.151:9090/api/v1/login,即请求后端真实的IP和端口(192.168.31.151:9090为后端的IP和端口),解决方式是通过在Response Headers中设置了Access-Control-Allow-Origin 属性。其请求/响应流程如下图所示。
(2)前端解决
前端解决跨域问题,稍显麻烦一点,例如我是用Vue脚手架搭建的前端工程,可以通过设置vue.config.js配置文件,具体如下。
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {
'/api': {
target: 'https://192.168.31.151:9090', // 后端服务器地址
changeOrigin: true, // 是否跨域
pathRewrite: { 、// 转发
'^/api': '/api'
},
secure: false
},
}
}
此时,再次点击界面登陆,和后端解决跨域问题一样,浏览器成功加载response数据。
采用前端代理配置方式解决Request URL为http://192.168.31.190:8080/api/v1/login(192.168.31.190:8080为前端的IP和端口),此时协议、域名、端口均相同,属于同源,跨域问题解决,其请求/响应流程如下图所示。