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); };