天天看點

JSONP實作跨域原理

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