首先介绍下jsonp原理
浏览器因为同源策略的限制,在不同源的服务器通过我们传统axios是不能直接用来请求数据的(忽略代理),而src标签则不受同源策略的影响,所以我们需要动态的创建带有src的标签让其进行数据的请求,这就是jsonp的原理,在src的URL地址末尾拼接上一个回调函数,用来接受服务器传回来的数据
前端jsonp的封装展示
1 //封装一个jsonp请求的函数 2 function query(opt) { 3 let str = "" 4 for (let key in opt) { 5 str += key + "=" + opt[key] + "&" 6 } 7 return str 8 } 9 //设置默认回调函数的名字 10 const defaultOptions = { 11 callbackName: "callback" 12 } 16 function jsonp(url, opt, options = defaultOptions) { 17 //参数解析 URL为访问的接口 opt为传播的数据 option 为接受参数的回调函数 18 return new Promise((resolve, reject) => { 19 //判断下这个?是不是存在 20 let index = url.indexOf("?"); 21 url += index != -1 ? query(opt) : "?" + query(opt); 22 url = url + `${options.callbackName}=${options.callbackName}`; 23 //首先创造一个标签 带有src的 24 const scriptDom = document.createElement("script"); 25 //设置其src属性 26 scriptDom.setAttribute("src", url); 27 //在window系统上创建一个回调函数用来接受数据 28 window[options.callbackName] = (res) => { 29 //在接受到了参数动态删除这个script节点和window上面的方法 30 delete window[options.callbackName]; 31 document.body.removeChild(scriptDom) 32 //接受成功后调用resolve 33 if (res) { 34 resolve(res) 35 } else { 36 reject("服务器暂没有获取到数据") 37 } 38 } 39 //动态创建script标记,错误的监听 40 scriptDom.addEventListener('error', () => { 41 delete window['jsonpCallback']; 42 document.body.removeChild(script); 43 reject('服务器加载失败!'); 44 }); 45 document.body.append(scriptDom) 46 }) 47 }
调用方式
1 <script> 2 // jsonp("http://localhost:7001/api", { 3 // user: "zhangsan", 4 // age: "18" 5 // }).then(res=>{ 6 // console.log(res); 7 // }).catch(err=>{ 8 // console.log((err,"失败")) 9 // }) 10 11 12 jsonp(" http://localhost:3000/api", { 13 user: "zhangsan", 14 age: "18" 15 }).then(res => { 16 console.log(res); 17 }).catch(err => { 18 console.log((err, "失败")) 19 }) 20 </script>
后端我们使用express和egg两款框架分别实现了接口的使用
express的代码展示
1 const url = require("url") 2 3 router.get("/api", (req, res, next) => { 4 //将script标签的src的URL请求转成对象 5 const opj = url.parse(req.url, true).query; 6 //然后原理就是调用这个回调函数来进行传参 7 let { 8 callback 9 } = opj; 10 //如果这个回调函数存在证明是jsonp请求 11 if (callback) { 12 let resault = JSON.stringify({ 13 code: 1, 14 msg: "express框架传回去的参数" 15 }); 16 res.send(`${callback}(${resault})`) 17 } 18 })
egg框架就不需要这么麻烦了 利用中间件可以直接出来
router.js代码
module.exports=app=>{ const {router,controller}=app; const jsonp = app.jsonp(); router.get("/api",jsonp,controller.index.api) //注意不要写成下面这种 // const {jsonp}=app; // router.get("/api",jsonp(),controller.index.api) } controller/index 代码
const {Controller}=require("egg"); class Index extends Controller{ api(ctx){ //直接利用body返回就会传到jsonp的回调函数里面 ctx.body={ code:11, type:"egg返回的jsonp请求" } } } module.exports=Index;
本文GitHup地址 https://github.com/qiang-chen/cross-domain