前言
在前后端分离开发模式下,Ajax跨域是一个常见的问题,本文梳理了一下Ajax跨域相关知识,做一个小总结
1.为什么会跨域?
浏览器同源政策及其规避方法(阮一峰)
浏览器为了保证用户信息的安全,防止恶意的网站窃取数据,引入同源策略,同源指的是"三个相同":
- 协议相同
- 域名相同
- 端口相同
如果非同源,AJAX 请求不能发送。
2.ajax跨域情景复现
案例一:
出现这种情况的原因如下:
本次ajax请求是“非简单请求”,所以请求前会发送一次预检请求(OPTIONS)
服务器端后台接口没有允许OPTIONS请求,导致无法找到对应接口地址
解决方案: 后端允许options请求
案例二:

这种现象和第一种和第二种有区别,这种情况下,服务器端后台允许OPTIONS请求,并且接口也允许OPTIONS请求,但是头部出现不匹配现象,因为我们自定义了头部x-token,而后端没有相应的设置
解决方案: 后端增加对应的头部支持
3.如何解决ajax跨域
一般ajax跨域解决就是通过JSONP解决或者CORS解决
JSONP方式解决跨域问题
JSONP解决跨域问题是一个比较古老的方案(实际中不推荐使用),这里做简单介绍(实际项目中如果要使用JSONP,一般会使用JQ等对JSONP进行了封装的类库来进行ajax请求)
实现原理
JSONP之所以能够用来解决跨域方案,主要是因为 <script> 脚本拥有跨域能力,而JSONP正是利用这一点来实现。具体原理如图
实现流程
JSONP的实现步骤大致如下(参考了来源中的文章)
客户端网页网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type","text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function () {
addScriptTag('http://example.com/ip?callback=foo');
}
function foo(data) {
console.log('response data: ' + JSON.stringify(data));
}; 请求时,接口地址是作为构建出的脚本标签的src的,这样,当脚本标签构建出来时,最终的src是接口返回的内容
服务端对应的接口在返回参数外面添加函数包裹层
foo({
"test": "testData"
}); 由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。
注意,一般的JSONP接口和普通接口返回数据是有区别的,所以接口如果要做JSONO兼容,需要进行判断是否有对应callback关键字参数,如果有则是JSONP请求,返回JSONP数据,否则返回普通数据
基于JSONP的实现原理,所以JSONP只能是“GET”请求,不能进行较为复杂的POST和其它请求,所以遇到那种情况,就得参考下面的CORS解决跨域了
CORS解决跨域问题
CORS的原理上文中已经介绍了,这里主要介绍的是,实际项目中,后端应该如何配置以解决问题(因为大量项目实践都是由后端进行解决的),这里整理了一些常见的后端解决方案:
PHP后台配置
<?php
// 主要为跨域CORS配置的两大基本信息,Origin和headers
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Methods:GET,POST,OPTIONS,DELETE");
header('Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept');CORS解决方案有一个缺点就是不可避免的OPTIONS请求,增加了网络损耗,可以使用以下配置缓存请求,减少OPTIONS请求次数:
header("Access-Control-Max-Age: 100");数值单位为秒代理请求方式解决接口跨域问题
与前面的方法不同,前面CORS是后端解决,而这个主要是前端对接口进行代理,也就是:
前端ajax请求的是本地接口,本地接口接收到请求后向实际的接口请求数据,然后再将信息返回给前端
一般用node.js即可代理,以webpack3.*版本为例,实现如下:
proxyTable: {
'/api':{
target: "http://example.com",
changeOrigin: true,
}
},也可以使用nginx代理,配置如下:
location /api {
proxy_pass http://example.com;
root html;
index index.html index.htm;
}
urepjk48652KQ-文章很不错,感谢作者!https://kan.xiaoxinbk.com/4683.html/