小言_互联网的博客

JavaScript基础:手写实现JSONP

258人阅读  评论(0)

一、原理

jsonp的核心原理是利用script标签没有同源限制的方式,可以发送跨域的get请求(只能发送get请求)。script标签中的src属性将请求参数和当前请求的回调函数名拼接在链接上。最终由服务端接收到请求之后拼接成前端可执行的字符串的形式返回。这个结果字符串最终会在前端的script标签中解析并执行。

利用<script>标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器做支持才可以。

  1. JSONPAJAX对比: JSONPAJAX相同,都是客户端向服务端发送请求,从服务端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)。
  2. JSONP优缺点: JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅仅支持get方法具有局限性,不安全,可能会遭受XSS攻击。

二、实现

1.正常实现

  1. jsonp回调函数的名称callbackName拼接到src
  2. 构建一个script标签,设置它的src属性
  3. 在全局设置一个callbackName回调函数,等待script标签请求结束,并调用
    /**
     * jsonp获取请求数据
     * @param {object}options
     */
    function jsonp(options) {
   
        // console.log(options);
        // 1. 产生不同的函数名(函数名随机)
        let callBackName = 'itLike' + Math.random().toString().substr(2)+Math.random().toString().substr(2);
        // console.log(callBackName);
        // 2. 产生一个全局函数
        window[callBackName] = function (params) {
   
            // console.log(params);
            // console.log(options.success);
            if(params !== null){
   
                options.success(params);
            }else{
   
                options.failure(params);
            }

            // 2.1 删除当前脚本标签
            scriptE.remove();
            // 2.2 将创建的全局函数设为null
            window[callBackName] = null;
        };

        // 3. 取出url地址
        let jsonpUrl;
        if(options.data !== undefined){
   
            jsonpUrl = options.url + '?' + options.data + '&callBack=' + callBackName;
        }else {
   
            jsonpUrl = options.url + '?callBack=' + callBackName;
        }
        // console.log(jsonpUrl);

        // 4. 创建script标签
        let scriptE = document.createElement('script');
        scriptE.src = jsonpUrl;
        document.body.appendChild(scriptE);
    }

服务端(express)

router.get('/api/v1', function(req, res, next) {
   
  res.json({
   
    'name': '前端收割机',
    'address': '广东',
    'intro': '前端技术交流公众号'
  });
});

调用jsonp

btn.addEventListener('click', ()=>{
   
        jsonp({
   
            url: 'http://localhost:3000/api/v1',
            data: 'name=前端收割机&age=20',
            success: function (data) {
      
                console.log(data);
            },
            failure:function(data){
   
                console.log(数据请求失败);
            }
        });
    });

2.利用promise封装

  1. 声明一个回调函数,其函数名(如show)当做参数值,要传递给跨域请求数据的服务器,函数形参为要获取目标数据(服务器返回的data)
  2. 创建一个 <script src=>标签 ,把那个跨域的API数据接口地址,赋值给scriptsrc, 还要在这个地址中向服务器传递该函数名(可以通过问号传参?callback=show)。
  3. 服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如:传递进去的函数名是show,它准备好的数据是 show('前端收割机')
  4. 最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作。
/**
* jsonp获取请求数据
* @param {string}url
* @param {object}params
* @param {function}callback
*/
function jsonp({
    url, params, callback }) {
   
    return new Promise((resolve, reject) => {
   
        let script = document.createElement('script');
        params = JSON.parse(JSON.stringify(params));
        let arrs = [];
        for (let key in params) {
   
            arrs.push(`${
     key}=${
     params[key]}`);
        }
        arrs.push(`callback=${
     callback}`);
        script.src = `${
     url}?${
     arrs.join('&')}`;
        document.body.appendChild(script);
        window[callback] = function (data) {
   
            resolve(data);
            document.body.removeChild(script);
        }
    })
}

服务器(express

// 后端响应
// 这里用到了 express
var express = require('express');
var router = express.Router();
var app = express();

router.get('/say',function(req,res,next) {
   
 //要响应回去的数据
  let data = {
   
    username : 'zs',
    password : 123456
  }

  let {
   wd , callback} = req.query;
  console.log(wd);
  console.log(callback);
  // 调用回调函数 , 并响应
  res.end(`${
     callback}(${
     JSON.stringify(data)})`);
})
app.use(router);
app.listen(3000);

调用jsonp

// 前端调用
btn.addEventListener('click', ()=>{
   
        jsonp({
   
    		url: 'http://localhost:3000/say',
    		params: {
   
        		wd: '前端收割机'
    		},
    			callback: 'show'
		}).then(data => {
   
    			console.log(data)
		});
    });

三、总结

基本原理:利用 script 标签的 src 没有跨域限制来完成实现。

优缺点:只能 GET;兼容性好。

简单实现:通过 url, params, callback 来定义 JSONP() 方法的参数。

参考连接:https://juejin.cn/post/6844904094973296654#heading-5


转载:https://blog.csdn.net/imagine_tion/article/details/115475157
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场