JSONP介绍
JSONP(JSON with Padding)是为了跨域获取资源而产生的一种非官方的技术手段,它利用的是script标签的src属性不受同源策略影响的特性,使网页可以得到从其他来源动态产生的json数据,因此可以用来实现跨域读取数据。
原理
JSONP的最基本的原理是:动态添加一个script标签,而script标签的src属性是没有跨域的限制的。
动态创建script标签,请求后端地址,后端经过处理,返回结果封装在callback参数里面,格式为callback参数(json)。
优点:浏览器兼容性好
缺点:只支持get请求
使用Script模式
var url = "http://localhost:8080/getPhoneNum";
function jsonpByScript() {
// 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码)
var newUrl = url + "?callback=pcall";
// 创建script标签,设置其属性
var script = document.createElement('script');
script.setAttribute('src', newUrl);
// 把script标签加入head,此时调用开始
document.getElementsByTagName('head')[0].appendChild(script);
}
//查询结果后的回调函数
function pcall(data) {
console.info("pcall", data);
}
jsonpByScript();
// 打印结果为
pcall {phoneNum: Array(2)}
// 请求返回结果
pcall({"phoneNum":["17011112222","17022223333"]})
浏览器显示信息
General
Request URL: http://localhost:8080/getPhoneNum?callback=pcall
Request Method: GET
Status Code: 200
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin
Response Headers
Date: Tue, 26 Apr 2022 10:13:15 GMT
Transfer-Encoding: chunked
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Host: localhost:8080
Referer: http://127.0.0.1:8848/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
使用Ajax模式
实际上还是使用script方式
//查询结果后的回调函数
function pcall(data) {
console.info("pcall", data);
}
function jsonpByAjax() {
$.ajax({
url: url,
type: "get",
data: {},
dataType: "jsonp",
jsonp: "callback", //传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonpCallback: "pcall", //自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
success: function(data) { //先调用pcall,在调用success
console.info("success", data);
}
})
}
jsonpByAjax();
// 打印结果
pcall {phoneNum: Array(2)}
success {phoneNum: Array(2)}
// 请求返回结果
pcall({"phoneNum":["17011112222","17022223333"]})
// 如果注释掉jsonpCallback: "pcall",结果样例
jQuery1830794611485514708_1650966351145({"phoneNum":["17011112222","17022223333"]})
浏览器显示信息
General
Request URL: http://localhost:8080/getPhoneNum?callback=pcall1650967838128
Request Method: GET
Status Code: 200
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin
Response Headers
Date: Tue, 26 Apr 2022 10:10:38 GMT
Transfer-Encoding: chunked
Request Headers
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Host: localhost:8080
Referer: http://127.0.0.1:8848/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: script
Sec-Fetch-Mode: no-cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
后台Java代码
try (PrintWriter writer = resp.getWriter();) {
String callback = req.getParameter("callback");
String jsonStr = "{\"phoneNum\":[\"17011112222\",\"17022223333\"]}";
writer.print(callback + "(" + jsonStr + ")");
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}
CORS(Cross-Origin Resource Sharing)跨域资源共享
CORS需要浏览器和服务器同时支持。目前,除了低于IE10浏览器不支持,多数浏览器都支持该功能。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX跨域请求,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨域访问。
两种请求
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
详情请参考跨域资源共享 CORS 详解
网页代码
var url = "http://localhost:8080/getPhoneNum2";
function ajax() {
$.ajax({
url: url,
type: "get",
data: {},
dataType: "JSON",
success: function(data) {
console.info("success", data);
}
})
}
ajax();
// 打印结果
success {phoneNum: Array(2)}
// 请求返回结果
{"phoneNum":["17011112222","17022223333"]}
浏览器显示信息
General
Request URL: http://localhost:8080/getPhoneNum2
Request Method: GET
Status Code: 200
Remote Address: [::1]:8080
Referrer Policy: strict-origin-when-cross-origin
Response Headers
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Content-Type, Cache-Control, Expires, X-E4M-With,userId,token
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Origin: http://127.0.0.1:8848
Access-Control-Max-Age: 0
Content-Type: application/json;charset=UTF-8
Date: Tue, 26 Apr 2022 11:45:27 GMT
Transfer-Encoding: chunked
Request Headers
Accept: application/json, text/javascript, */*; q=0.01
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Host: localhost:8080
Origin: http://127.0.0.1:8848
Referer: http://127.0.0.1:8848/
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="98", "Google Chrome";v="98"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36
后台Java代码
// resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8848");
resp.setHeader("Access-Control-Allow-Methods", "GET, POST");
resp.setHeader("Access-Control-Max-Age", "0");
resp.setHeader("Access-Control-Allow-Headers",
"Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Content-Type, Cache-Control, Expires, X-E4M-With,userId,token");
resp.setHeader("Access-Control-Allow-Credentials", "true");
resp.setHeader("Content-type", "application/json; charset=UTF-8");
try (PrintWriter writer = resp.getWriter();) {
String jsonStr = "{\"phoneNum\":[\"17011112222\",\"17022223333\"]}";
writer.print(jsonStr);
writer.flush();
} catch (Exception e) {
e.printStackTrace();
}