Skip to main content

Command Palette

Search for a command to run...

Jsonp 跨域解决方案解析

Published
2 min read
Jsonp 跨域解决方案解析
T

A simple developer.

由来背景

As we all know,浏览器同源策略会将非同源请求(跨域)抛弃,而许多时候我们可能并未将前端资源与服务端服务放在一个服务器,此时就需要一个跨域的手段了。

基于此,针对同源策略,衍生了一种跨域的方法Jsonp。

Jsonp简单讲,即利用了script标签不受浏览器同源策略影响的特性,从而利用script想服务端跨域请求的方式。

原理解析

图下图所示

image.png

封装一个Jsonp函数,Promise化

// jsonp.ts
type JsonpOptions<T extends Record<string, any>> = {
    url: string,
    timeout: number,
    data: T,
    cbKey?: string
};

interface JsonpRes {
    code: 0 | -1,
    data: any,
    msg: string
}

export default function createJsonp<T extends Record<string, any>> (options: JsonpOptions<T>): Promise<JsonpRes> {
    return new Promise((resolve, reject) => {

        // 第一次请求,初始化随机数
        if (!jsonpNumber) {
            jsonpNumber = 1;
        } else {
            jsonpNumber++; // 拼接到callbackFn上,以防止多个jsonp请求时错误使用同一个callback
        }

        // callback接收函数名
        const jsonpReceiverFnName = 'jsonpReceiver' + jsonpNumber;
        const {
            url,
            timeout,
            data,
            cbKey = 'callback'
        } = options;
        let script: HTMLScriptElement; // 将被插入DOM的script标签

        // 设置超时定时器
        let timeoutTimer = setTimeout(() => {
            reject({
                code: -1,
                data: null,
                msg: '请求超时'
            });
            clear(); 
        }, timeout);

        // 清除上一次请求存的变量/定时器
        function clear () {
            window[jsonpReceiverFnName] = null; // 删除callbackFn
            clearTimeout(timeoutTimer); // 清除定时器
            script.parentNode?.removeChild(script); // 移除当前jsonp的script
        }

        window[jsonpReceiverFnName] = (res) => {
            resolve({
                code: 0,
                data: res,
                msg: '请求成功'
            }); // 将数据返回到
            clear(); // 请求成功后,清理本次jsonp带来的“副作用”
        }

        // 创建JSONP请求标签,发出请求
        let params = ''; // 仅支持get请求,故只有query参数

        // 遍历data,拼接参数
        for (let key in data) {
            if (data.hasOwnProperty(key)) {
                params += `&${key}=${data[key]}`;
            }
        }
        params = params.substring(1); // 移除第一个 &

        script = document.createElement('script');
        script.src = `${url}?${params}&${cbKey}=${jsonpReceiverFnName}`; // 拼接请求url
        document.body.appendChild(script); // 插入body,正式发出jsonp请求
    });
};
// 调用jsonp函数
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="./jsonp.js"></script>
</head>

<body>
    <script>
        async function testJsonp() {
            try {
                const res = await createJsonp({
                    url: 'https://x.x.x.x/testjsonp',
                    timeout: 15000,
                    data: {
                        id: '00145x',
                        level: 2
                    }
                });

                !res.code && console.log(res.data)
            } catch (rejectRes) {
                rejectRes.code && console.log(rejectRes?.msg)
            }
        }

        testJsonp();
    </script>
</body>

</html>

More from this blog

Git 无法正常访问问题解决 —— DNS无法解析git地址

在国内网络经常会无法访问github,一般呈现错误为: github.com 打不开 git 操作 clone/push/pull/fetch 超时等 这普遍由于国内DNS解析github相关域名的问题,国内DNS解析污染会导致,无法解析github相关域名,使得我们无法获悉正确ip而无法访问。 偏方一剂:修改Host,固定绑定 domain - ip 查询相关域名的解析 IPAddress github.com github.global.ssl.fastly.net asse...

Sep 22, 20221 min read

Vue SSR实战小练

思想 开发环境: 在webpack dev与pro的前端打包构建基础上, 添加webpack server compiler的服务,其为单独创建的node服务,用于渲染html代码并返回给客户端。(其他JS...则仍旧交由webpack dev server来构建) 所以在获取html后要再自行将dev客户端渲染的js加入到html中 在生产环境则不需如此: 由dev与server打包好的文件,将其组合 客户端则访问node服务来获取文件 基本用法 安装:npm install v...

Sep 5, 20229 min read

唯在一心

15 posts

A simple developer.