jsonp原理:javascript
因爲瀏覽器同源策略的約束,非同源的狀況下都會產生跨域,可是在HTML中有哪些元素天生就是能夠跨域的,例如<img>,<link>,<script>,這些標籤都是能夠引入外部資源的,他們統一的特色就是擁有src/href屬性,實際上是這些屬性在跨域,而不是標籤自己在跨域,也就是說凡是能夠引入外部資源的屬性或者標籤都是能夠實現跨域的。php
jsonP跨域其實也就是利用<script>標籤中的src屬性html
jsonp實現原理:前端
首先在客戶端註冊一個callback,而後把callback的名字傳遞給服務端。java
此時服務器生成json數據。git
而後後臺能夠經過es6的語法拼接一個字符串,生成一個function,fuction的名字就是callback的名字es6
最後將json數據直接以入參的方式,放到function中,返回到客戶端。github
客戶端瀏覽器,解析script標籤,此時數據做爲參數,傳入到客戶端預先定好的callback函數中(動態執行回調函數)express
直接上代碼json
//用於拼接src路徑 //<script src="http://www.bbb.com/index.php?name="aaa"&age="12"callback=getData"></script> function string(params) { let str = ""; for (var key in params) { str += `${key}=${params[key]}&`; } str += "callback=jsonCallback"; return str; } //設置默認回調函數的名字 const defalutConfig = { callbackname: "jsonCallback" }; const jsonp = (url, params, option = defalutConfig) => { // 返回一個能夠鏈式調用的promise對象 return new Promise((resolve, reject) => { //將前端傳遞querystring查詢字符串的參數,拼接到地址欄 url = url.indexOf("?") === -1 ? url + "?" + string(params) : string(params); //動態建立script標記 const script = document.createElement("script"); //設置接口的請求地址 script.setAttribute("src", url); //設置請求jsonp接口的回調函數 window[option.callbackname] = res => { //請求jsonp接口成功後,刪除該函數 - 不污染window delete window[option.callbackname]; //從頁面中刪除請求接口動態建立的script標記 document.body.removeChild(script); //判斷接口的數據返回 if (res) { resolve(res); } else { reject("服務器沒有返回數據"); } }; //動態建立script標記,錯誤的監聽 script.addEventListener("error", () => { delete window["jsonpCallback"]; document.body.removeChild(script); reject("服務器加載失敗!"); }); document.body.appendChild(script); }); };
html文件調用方式
//引入jsonp <script src="./index.js"></script> <script> jsonp("http://localhost:3000/api",{ name:"panhuij", age:20 }).then(res=>{ console.log(res) }).catch(err=>{ console.log(err,"err....") }) </script>
express後臺
router.get("/api", (req, res) => { //獲取url路徑傳遞的參數 let ourl = url.parse(req.url); let oquery = querystring.parse(ourl.query); console.log(ourl) //判斷是否有callback,參數證實是jsonp請求 let { callback } = oquery; if (callback) { res.writeHead(200, { "Content-Type": "text/javascript" }); const data = { code: 1, type: "jsonp", ...oquery }; //將數據拼接到函數裏面傳遞回去 res.end(callback + "(" + JSON.stringify(data) + ")"); } else { res.writeHead(200,{ "Access-Control-Allow-Origin":"*" }) const data = { code: 1, type: "json", ...oquery }; res.end(JSON.stringify(data)) } });
egg後臺 直接利用中間件
//router.js module.exports = app => { const { router, controller } = app; const jsonp=app.jsonp() router.get("/api", jsonp,controller.home.api); };