前言
首先我们得先了解JSONP是怎么产生的。
最开始跨域请求数据没有现在方便,Ajax直接请求普通文件存在跨域无权限访问的问题,然后聪明的程序员想出了一套非官方的解决办法,程序员发现凡是带有“src”这个属性的标签都拥有跨域的能力,比如<·img>、<·iframe>、<·script>。
事实上早期的程序员也是这么干的,最后程序员们发现最好的解决办法就是——动态创建script标签发起请求,然后从后端拿到请求回来的数据进行处理,再然后把刚刚创建的script标签删掉,这就是JSONP的整套流程。做完这一切,在用户的角度是感觉不到动态创建script标签以及发送请求的,用户体验非常好,因此JSONP提供了一种各方都很满意的跨域解决方案。
何为JSONP
JSONP是JSON with Padding的略称,JSONP为民间提出的一种跨域解决方案,通过客户端的script标签发出的请求方式。
一、什么是JSONP(JSON With Padding)?
动态添加<script>来调用服务器提供的js脚本。
这是一种非正式的传输方式,由于发现js文件不受同源策略的影响,包括含有src的标签都不受影响。所以通过web动态添加<script>,通过src调用服务器同时带上一个callback的参数,服务器创建一个包裹返回数据的函数,函数名就是callback的参数,最终返回一个可执行的js函数调用。
二、如何使用JSONP解决跨域问题?
现有如下两个服务器:A: http://localhost:8080 和B:http://localhost:8888,浏览器访问地址:
http://localhost:8080/mybatis/jsp/test.jsp
1、正常ajax访问,出现的跨域问题
访问 http://localhost:8080/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8080/mybatis/home',
type : 'get',
data : {
name : "8080"
},
success : function(resp) {
console.log(resp);
}
});
服务器:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
resp.getWriter().append("welcome "+ name + " !");
}
结果:正常访问
访问http://localhost:8888/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8888/mybatis/home',
type : 'get',
data : {
name : "8888"
},
success : function(resp) {
console.log(resp);
}
});
服务器:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
resp.getWriter().append("welcome "+ name + " !");
}
结果:不能访问
因为同源策略,端口不同出现跨域访问。
2、动态创建script
访问http://localhost:8888/mybatis/home
前端代码:
$("head").append("<script src='http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript'><\/script>");
//或者如下js
//var sc = document.createElement("script");
//sc.src = "http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript";
//document.body.insertBefore(sc,document.body.firstChild);
function testscript(resp) {
console.log(resp);
}
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("callback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
通过js创建一个<script>标签,同时在请求的地址中拼接一个callback=testscript的参数;
在服务器中,获取callback的参数值,与返回数据拼装成一个可执行的函数:testscript("+返回的数据+");
返回到web后自动执行testscript函数。
3、jquery的JSONP方式
访问http://localhost:8888/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
// jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
// jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log(resp);
}
});
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("callback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
只需要在ajax方法里,添加一个dataType:'jsonp'参数即可。乍一看,咋没有回调函数的名称,其实查看url就知道了,
4、jsonp全部参数
访问http://localhost:8888/mybatis/home
前端代码:
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log("ajax:"+resp);
}
});
function othername(resp){
console.log("othername:"+resp);
}
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("othercallback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
5、使用getJSON
访问http://localhost:8888/mybatis/home
前端代码:
$.getJSON("http://localhost:8888/mybatis/ScriptServlet?name=getJSON&othercallback=?",function(resp){
console.log(resp);
});
服务器:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String callback = request.getParameter("othercallback");
String name = request.getParameter("name");
response.getWriter().append(callback+"(\""+name+"\")");
}
结果:可以访问
6、前端全部代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>This is 8080 page</title>
</head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js">
</script>
<script type="text/javascript">
$(function() {
$("a").click(function() {
switch ($(this).attr("id")) {
case "1":
$.ajax({
url : 'http://localhost:8080/mybatis/home',
type : 'get',
data : {
name : "8080"
},
success : function(resp) {
console.log(resp);
}
});
break;
case "2":
$.ajax({
url : 'http://localhost:8888/mybatis/home',
type : 'get',
data : {
name : "8888"
},
success : function(resp) {
console.log(resp);
}
});
break;
case "3":
$("head").append("<script src='http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript'><\/script>");
// var sc = document.createElement("script");
// sc.src = "http://localhost:8888/mybatis/ScriptServlet?name=script&callback=testscript";
// document.body.insertBefore(sc,document.body.firstChild);
break;
case "4":
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
// jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
// jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log(resp);
}
});
break;
case "5":
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'get',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log("ajax:"+resp);
}
});
break;
case "6":
$.getJSON("http://localhost:8888/mybatis/ScriptServlet?name=getJSON&othercallback=?",function(resp){
console.log(resp);
});
default:
break;
}
});
});
function othername(resp){
console.log("othername:"+resp);
}
function testscript(resp) {
console.log(resp);
}
</script>
<body>
<a href="javascript:void(0)" id="1">访问本homeservlet</a>
<br />
<a href="javascript:void(0)" id="2">访问8888 homeservlet</a>
<br />
<br />
<a href="javascript:void(0)" id="3">动态创建script</a>
<br>
<a href="javascript:void(0)" id="4">jquery跨域请求</a>
<br>
<a href="javascript:void(0)" id="5">jquery跨域请求(自定义callback)</a>
<br>
<a href="javascript:void(0)" id="6">getJSON</a>
<br>
</body>
</html>
五、JSONP主要弊端,缺陷
1、不支持post方式,即使在ajax中改成post,实际也是get方法
将以上ajax中的get改成post:
$.ajax({
url : 'http://localhost:8888/mybatis/ScriptServlet',
type : 'post',
data : {
name : 'test 8888,jsonp'
},
dataType : 'jsonp',
// jsonp:'othercallback', //指定参数名称,即callback改为othercallback,所以后台获取相应也要改为request.getParameter("othercallback")
// jsonpCallback:'othername', //指定回调函数名称
success : function(resp) {
console.log(resp);
}
});
看起来请求成功了,但是实际最终还是get请求,
因为jsonp是通过动态创建script,而动态生成script的src只能用get。
2、JSONP是一种脚本注入,存在一定的安全隐患。
安全问题可以参考:https://blog.csdn.net/u014021893/article/details/72303110