最近在做我星际schub网站的时候,遇到了跨域问题,我先把后端node部署在了服务器上,然后在本地localhost测试,出现了问题:
浏览器都提示我们使用这个header头:
解决办法:
###1. CORS
服务器设置响应头:
response.setHeader("Access-Control-Allow-Origin", "*")
(这样可能引起CSRF攻击,一般设置成对应的域名就行,
response.setHeader(“Access-Control-Allow-Origin”, “http:localhost:8080/”) )
fetch的option加上 mode:“cors”。
(需要注意的是,如果要发送Cookie,Access-Control-Allow-Origin就不能设为星号,必须指定明确的、与请求网页一致的域名。同时,Cookie依然遵循同源政策,只有用服务器域名设置的Cookie才会上传,其他域名的Cookie并不会上传,且(跨源)原网页代码中的document.cookie也无法读取服务器域名下的Cookie。)
关于CORS的讲解,可以看阮一峰的这篇文章:链接
###2.jsonp
jsonp的方式,注意这种方式只能发送get请求,就算指定为post,最后发送的还是get,且跟上面方式一样,也需要服务器的配合支持。之前在做vue音乐播放器项目的时候,调用的是qq音乐的接口,qq音乐的接口返回的就是jsonp格式的
1、 jsonp发送的并不是ajax请求;
2、 利用动态创建一个script标签,因为script标签是没有同源策略限制的,是可以跨域的;
3、 把这个script标签的src指向我们请求的服务端地址,这个地址会携带一个参数:callback ,一个回调函数,服务端会把数据通过这个回调函数返回给客户端,但是客户端没有这个函数怎么接收呢?所以在发送请求之前,要在全局(window)注册这样一个方法,利用这个方法,来获得数据;
4、 这个回调函数名需要跟服务端约定好,是一致的。
如果让我们用原生js实现一个jsonp,那就是这样:
<script>
//指定的回调函数
function showData (result) {
var data = JSON.stringify(result); //json对象转成字符串
$("#text").val(data);
}
$(document).ready(function () {
$("#btn").click(function () {
//向头部输入一个脚本,该脚本发起一个跨域请求
$("head").append("<script src='http://localhost:9090/student?callback=showData'><\/script>");
});
});
</script>
后端一是获取到前端传过来的回调函数名称,二是获取到数据,最后把数据用回调函数包围住,执行这个前端的这个回调函数。我们就可以利用前端这个事先全局注册的回调函数来显示操作数据了。
用Java写的后台demo就是这样:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//数据
List<Student> studentList = getStudentList();
JSONArray jsonArray = JSONArray.fromObject(studentList);
String result = jsonArray.toString();
//前端传过来的回调函数名称
String callback = request.getParameter("callback");
//用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了
result = callback + "(" + result + ")";
response.getWriter().write(result);
}
这里再分析一下jQuery关于jsonp的实现。
1.最简单的方式:
$.ajax({
type:"GET",
url:"http://www.xxx.com/getMySeat", //访问的链接
dataType:"jsonp", //数据格式设置为jsonp
success:function(data){ //成功的回调函数
alert(data);
},
error: function (e) {
alert("error");
}
});
服务端代码不变,最简单的方式,只需配置一个dataType:‘jsonp’,就可以发起一个跨域请求。jsonp指定服务器返回的数据类型为jsonp格式,可以看发起的请求路径,自动带了一个callback=xxx,callback是自己不手动设置参数名字的默认值,xxx是jquery随机生成的一个回调函数名称。
这里的success就跟上面的showData一样,如果有success函数则默认success()作为回调函数。
2.回调函数你可以写到script下(默认属于window对象),或者指明写到window对象里,看jquery源码,可以看到jsonp调用回调函数时,是调用的window.callback。
然后看调用结果,发现,请求时带的参数是:callback=showData;调用回调函数的时候,先调用了指定的showData,然后再调用了success。所以,success是返回成功后必定会调用的函数,就看你怎么写了。
指定回调函数名称,加一个option字段即可:
最后还是会调用success:
如何改变callback这个默认值(回调函数名称的key值),在jq里:
这样一改之后,响应的后端代码也要改动了,获取的字段key值就是theFunction了,
对应的请求路径也就变了:
除了jq对jsonp的实现,在上面提到的vue音乐播放器项目中,我使用了jsonp npm包,用这个包,就不需要自己在window上注册回调函数,qq音乐接口中jsonpCallback就是服务端返回的回调函数(a方法) 我们本地在window上注册的a方法是在调用的jsonp库中自动被实现。我们自己写成promise的方法后直接then即可。
但是后来我又遇到一个问题,schub星际网站项目中我想调用WCS职业联赛积分赛排名的接口的时候,用fetch当然还是提示跨域问题了,这时候后端我又不能修改,所以CORS方式肯定不行,jsonp的方式也需要后端配合支持,上面两种方法都不适用了。下面就是最终解决问题的第三种方法。
代理服务器,跨域问题是浏览器的同源策略限制,服务器之间是没有的,那我们先把请求给我们的代理服务器,再让我们的代理服务器去调用这个接口,在发送给前端就可以了。
前端:
代理服务器(node):
注意服务器返回的数据在response.data里,然后发送给前端的响应res。
(后来我前后端设置的端口不同,代理服务器也要设置Access-Control-Allow-Origin的头为 *)
###4.第四点我想谈下不同跨域方式中关于cookie的。
在后面的博客将会谈到cookie再跨域中的问题。