跨域
协议、域名、端口都相同才算同域,否则就是跨域。
浏览器为了安全考虑(同源策略),不允许 axaj 跨域获取数据。浏览器会报错 xxx has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
。
但是可以跨域获取文件内容,例如 script 标签、 img 标签可以跨域使用,利用这一点,就可以用 JSONP 进行跨域请求,解决跨域问题。
JSONP
JSONP ( JSON with Padding ) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。
JSONP 由两部分组成:回调函数+数据 。
JSONP 的原理就是动态添加一个 script 标签,而 script 标签的 src 属性是没有跨域的限制的。
后台代码
后台需要用回调函数名称包裹返回数据,这样,返回数据就作为回调函数的参数传回去了。
传统方法:
@RequestMapping("/test")
@ResponseBody
public String test(String callback) {
Map<String, String> map = new HashMap<String, String>();
map.put("msg", "hello world!");
return callback + "(" + JSON.toJSON(map) + ")";
}
spring 4.1 后新特性 :
@RequestMapping("/test")
@ResponseBody
public Object test(String callback) {
Map<String, String> map = new HashMap<String, String>();
map.put("msg", "hello world!");
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(JSON.toJSON(map));
mappingJacksonValue.setJsonpFunction(callback);
return mappingJacksonValue;
}
前端调用
1.利用 script 标签调用
<script type="text/javascript">
function jsonpCallback(result) {
console.log(result);
}
</script>
<script type="text/javascript" src="http://192.168.10.147:8080/zyjyly-1.0/dataApplication/test.do?callback=jsonpCallback"></script>
客户端注册一个 callback ,这里是 jsonpCallback ,然后把 callback 的名字传给服务器,服务端得到这个函数名之后,要用这样的方法 jsonpCallback(...)
包裹要输出的 json 内容。 此时服务器生成的json数据才能被客户端正确接收。
运行结果:
{msg: "hello world!"}
2.jQuery 的实现(普通 ajax 方式)
原理是一样的,只不过我们不需要手动的插入 script 标签以及定义回调函数。
jquery 会自动生成一个全局函数来替换 callback=? 中的问号,之后获取到数据后又会自动销毁,实际上就是起一个临时代理函数的作用。
<script type="text/javascript">
$(function() {
$.ajax({
type:'get',
dataType: 'jsonp',
url: 'http://192.168.10.147:8080/zyjyly-1.0/dataApplication/test.do',
success: function (result) {
console.log(result);
}
});
});
</script>
需要注意下,jsonp 只能用 get 方式去提交。
运行结果:
{msg: "hello world!"}
我们调试下后台代码,可以看到,jquery 会自动生成一个函数,去替换 callback=? 中的问号。
当然,我们也可以自定义 callback 的参数名和回调函数的名称。
<script type="text/javascript">
$(function() {
$.ajax({
type:'get',
dataType: 'jsonp',
jsonp: "myCallBack", // 指定 callback 的参数名
jsonpCallback: "showData", // 指定回调函数的名称
url: 'http://192.168.10.147:8080/zyjyly-1.0/dataApplication/test.do',
success: function (result) {
console.log("success");
}
});
});
function showData(result){
console.log(result);
}
</script>
这时候,后台方法的参数就要稍微修改下:
@RequestMapping("/test")
@ResponseBody
public Object test(String myCallBack) {
Map<String, String> map = new HashMap<String, String>();
map.put("msg", "hello world!");
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(JSON.toJSON(map));
mappingJacksonValue.setJsonpFunction(myCallBack);
return mappingJacksonValue;
}
运行结果如下:
{msg: "hello world!"}
success
3.jQuery 的实现( $.getJSON 方式)
$.getJSON 方法会自动判断是否跨域,不跨域的话,就调用普通的 ajax 方法;跨域的话,则会以异步加载 js 文件的形式来调用 jsonp 的回调函数。
<script type="text/javascript">
$(function () {
$.getJSON("http://192.168.10.147:8080/zyjyly-1.0/dataApplication/test.do?callback=?", function (result) {
console.log(result);
});
});
</script>
需要注意下,参数后面必须带上 callback=? 。
JSONP 的弊端:
- 要对服务器的代码进行改动
- 只支持 GET 方法(原理是动态创建 script 来进行请求的)
- 发送的不是 XMLHttpRequest 请求( XMLHttpRequest 请求有很多好用的特性)
总结
一句话就是利用 script 标签绕过同源策略,获得一个类似这样的数据,callback 是页面存在的回调方法,参数就是想得到的 json。
callback({"msg": "hello world!"})