浏览器只对XMLHttpRequest请求有同源请求限制,而对script标签src属性、link标签ref属性和img标签src属性没有这这种限制,利用这个“漏洞”就可以很好的解决跨域请求。JSONP就是利用了script标签无同源限制的特点来实现的。当然需要后端服务器的配合,返回一个合法的JS脚本,一般是一条调用js函数的语句,数据作为函数的入参。
看下面的例子:
接口:
@RestController
public class ApiTestController {
@GetMapping("/api/test")
public void index(String callback ,HttpServletResponse response) throws Exception {
//response.setHeader("Access-Control-Allow-Origin","*");
HashMap<String, Object> map = new HashMap<>();
map.put("hello","test");
map.put("name","test");
map.put("age",11);
map.put("sex","f");
Object o = JSON.toJSON(map);
response.getWriter().write(callback+"("+ o +")");
}
}
这个Controller很简单,负责将数据包装成callback({})的形式,返回客户端,最终的数据是callback({hello:'test',name:'test',age:11,sex:'f'}),从js的语法来看,这不就是一个简单的函数调用吗?其中函数的参数已经设置好了,就是接口要返回的数据,对于callback是由前端请求的一个参数,代表前端js使用的回调函数。
前端代码:
<script>
function jsonp(data) {
console.log(data)
}
</script>
<script src="http://localhost/api/test?callback=jsonp"></script>
在前面说了,浏览器对script 标签发送的请求没有跨域限制,所以使用script标签对接口发起请求,服务器端接收请求之后返回这样的数据jsonp({hello:'test',name:'test',age:11,sex:'f'}) ,由于是script标签发送的请求,所以浏览器会将响应当作js脚本执行,也就是调用jsonp函数,这样数据就拿到了。需要注意的是,接口返回的字符串必须是合法的js语句,否则浏览器执行js时会报错。
JSONP 使用简单且兼容性不错,但是只限于
get
请求。在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现:
function jsonp(url, jsonpCallback, success) {
let script = document.createElement("script");
script.src = url;
script.async = true;
script.type = "text/javascript";
window[jsonpCallback] = function(data) {
success && success(data);
};
document.body.appendChild(script);
}
jsonp(
"http://xxx",
"callback",
function(value) {
console.log(value);
}
);