浏覽器隻對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);
}
);