jsonp原理:
由于浏览器同源策略的约束,非同源的情况下都会产生跨域,但是在HTML中有哪些元素天生就是可以跨域的,例如<img>,<link>,<script>,这些标签都是可以引入外部资源的,他们统一的特点就是拥有src/href属性,其实是这些属性在跨域,而不是标签本身在跨域,也就是说凡是可以引入外部资源的属性或者标签都是可以实现跨域的。
jsonP跨域其实也就是利用<script>标签中的src属性
jsonp实现原理:
首先在客户端注册一个callback,然后把callback的名字传递给服务端。
此时服务器生成json数据。
然后后台可以通过es6的语法拼接一个字符串,生成一个function,fuction的名字就是callback的名字
最后将json数据直接以入参的方式,放到function中,返回到客户端。
客户端浏览器,解析script标签,此时数据作为参数,传入到客户端预先定好的callback函数中(动态执行回调函数)
直接上代码
//用于拼接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); };