iT邦幫忙

0

Day 1 Jsonp 跨域解決方案分析

  • 分享至 

  • xImage
  •  

背景

As we all know,瀏覽器同源策略會將非同源請求(跨域)拋棄,而許多時候我們可能併未將前端資源與服務端服務放在一個服務器,此時就需要一個跨域的手段了。

基於此,針對同源策略,衍生了一種跨域的方法Jsonp。

Jsonp簡單講,即利用了script標簽不受瀏覽器同源策略影響的特性,從而利用script想服務端跨域請求的方式。

原理解析

封裝一個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>

That's all. Shuan Q


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言