前端向后端發(fā)送請(qǐng)求,JSONP學(xué)習(xí)筆記

原始時(shí)代

在最早的時(shí)候,當(dāng)我們需要向服務(wù)器提交請(qǐng)求時(shí),用的是<form>表單提交到服務(wù)器,服務(wù)器接收到請(qǐng)求并且返回響應(yīng)后,瀏覽器的頁面是會(huì)刷新的。也就是說我們每次更新一個(gè)數(shù)據(jù),都需要刷新當(dāng)前頁面一次,這種行為是無法忍受的。

為了解決這個(gè)問題,前輩們最先想到的方法就是使用一個(gè)<iframe>,將刷新結(jié)果放在<iframe>中顯示,這樣我們頁面瀏覽就不會(huì)受到太大的影響。

    <form action="/pay" method="POST" target="result">
        <input type="submit" value="付款">
    </form>
    <iframe src="about:blank" frameborder="0" height="200px" name="result"></iframe>

<iframe>的各種缺點(diǎn)就不提了,隨著技術(shù)的發(fā)展與進(jìn)步現(xiàn)在幾乎沒有多少網(wǎng)頁會(huì)再使用<iframe>了。

于是乎為了避免使用<iframe>,前輩們又思考是否可以不用<form>發(fā)送請(qǐng)求呢?
我們發(fā)現(xiàn)<img> <link> <a>,這些標(biāo)簽都是通過向服務(wù)器發(fā)送請(qǐng)求以獲取資源的,也就是說我們可以用這些標(biāo)簽代替<form>向服務(wù)器發(fā)送一個(gè)請(qǐng)求。

用img構(gòu)建GET請(qǐng)求

img的加載就是一個(gè)GET請(qǐng)求,如果我們用img來加載一個(gè)假的圖片的話,就可以變相偽造一個(gè)請(qǐng)求了。

    <button id="button">付款</button>
    <script>
        button.onclick = ()=> {
            let img = document.createElement("img");
            img.src = "/pay"
            img.onload = ()=> {
                alert("付款成功")
            }
            img.onerror = () => {
                alert("付款失敗")
            }
        }
    </script>

這樣我們就可以通過動(dòng)態(tài)創(chuàng)建一個(gè)img設(shè)置它的src為服務(wù)器請(qǐng)求地址,來構(gòu)造一個(gè)GET請(qǐng)求(使用這種方法無法產(chǎn)生POST請(qǐng)求)。這樣后端再做一些處理(例如當(dāng)成功時(shí)返回一個(gè)1x1的透明圖片),前端就可以通過onload和oneror來判斷請(qǐng)求成功與否。

用script構(gòu)建GET請(qǐng)求

<img>一樣,<script>也可以通過src來訪問服務(wù)器。

    <button id="button">付款</button>
    <script>
        button.onclick = ()=> {
            let spt = document.createElement("script");
            spt.src = "/pay";
            document.body.appendChild(spt);

            spt.onload = ()=> {
                alert("付款成功");
            }
            spt.onerror = () => {
                alert("付款失敗");
            }
        }
    </script>

<img>不同的是,動(dòng)態(tài)創(chuàng)建的<script>必須插入到html中才能生效。
與上面一種方法相比較,動(dòng)態(tài)創(chuàng)建js的方式不需要添加一個(gè)無謂的圖片來浪費(fèi),但是js也是有自己的副作用的,那就是服務(wù)器返回的js代碼一定會(huì)執(zhí)行(因?yàn)樗呀?jīng)添加的html中了),由此我們可以延伸出直接通過后端來響應(yīng)頁面的成功請(qǐng)求。
我們前端就不需要像之前那樣監(jiān)聽onload事件了,因?yàn)楹蠖朔祷氐?code><script>會(huì)先執(zhí)行,我們?cè)谀巧厦鎴?zhí)行成功代碼就可以了。
因?yàn)槊看雾憫?yīng)都會(huì)在html添加一個(gè)<script>非常不美觀,所以我們?cè)趏nload里把至這些新插入的標(biāo)簽移除就可以了。

button.onload = (e)=>{
    e.currentTarget.remove();
}

這種方案通過動(dòng)態(tài)創(chuàng)建<script>來構(gòu)造GET請(qǐng)求的方法被叫做SRJ, server render javascript。

JSONP跨站請(qǐng)求

我們發(fā)現(xiàn),使用html的帶有src屬性的標(biāo)簽都可以構(gòu)造一個(gè)GET請(qǐng)求(不能構(gòu)造POST),并且這個(gè)請(qǐng)求是可以跨站的。
于是,基于SRJ便誕生出了利用<script>來構(gòu)建跨站請(qǐng)求的方法JSONP:JSON with PADDING
雖然名字里帶了JSON但這個(gè)JSONP和JSON沒有半毛錢關(guān)系,不要誤會(huì)了,JSONP是一種跨站請(qǐng)求方案的名字。

那么什么是JSONP呢?

我們前端發(fā)送跨站請(qǐng)求時(shí),查詢參數(shù)附上一個(gè)我們前端定好的函數(shù)。

    <script>
        var fuckBrowser = (response)=>{
            if (response === 'sucess'){
                console.log("scuess");
            }
        }
        var script = document.createElement("script");
        script.src = 'http://www.domain.com/?callbackName=fuckBrowser'
    </script>

對(duì)面的后端接收的GET請(qǐng)求后作出判斷,若成功,則發(fā)送響應(yīng),這個(gè)響應(yīng)的內(nèi)容就是讓我們這邊的前端執(zhí)行查詢參數(shù)上的函數(shù)。

//后端響應(yīng)
if(響應(yīng)成功條件滿足){
  //這里后面的callback就是url里的?callback=''
    var callbackFunctionName = request.query.callback;
  //這樣后端就可以知道前端定好的函數(shù)是fuckBrowser
  // callbackFunctionName === fuckBrowser  /*true*/
  response.write('callbackFunctionName.call(undefined, 'scuess')');
  //這樣前端就會(huì)執(zhí)行fuckBrowser并且第二個(gè)參數(shù)時(shí)scuess
}

這就是一個(gè)簡(jiǎn)單JSONP例子,這樣就可以達(dá)到前后端分離的跨站請(qǐng)求,不過我們還是要遵循一些業(yè)界的規(guī)定。

  1. 這個(gè)回調(diào)函數(shù)的參數(shù)名統(tǒng)一命名為callback
  2. callback的函數(shù)名為隨機(jī)數(shù)

上面的后端如果返回的不是’scuess’而是一個(gè)json對(duì)象,那么這就符合最初JSONP設(shè)計(jì)的初衷了,不過現(xiàn)在的JSONP更多指的是這樣一種跨站請(qǐng)求解決方法的形式。

JSONP總結(jié)

JSONP的全稱是 json with padding,它是利用SRJ(服務(wù)器響應(yīng)創(chuàng)建js)技術(shù)來達(dá)成發(fā)送跨站請(qǐng)求。

  1. 前端首先定義好一個(gè)用來接收后端數(shù)據(jù)的functionName(參數(shù)1, 參數(shù)2)
  2. 前端在發(fā)送查詢請(qǐng)求的URL中指定一個(gè)callback參數(shù)?callback=fuctionName,并且這個(gè)functionName應(yīng)該是由隨機(jī)數(shù)組成(防止重名與緩存)
  3. 后端通過request解析callback的名字(后端并不需要知道它叫什么)
  4. 后端返回一個(gè)response,這個(gè)response是要求前端執(zhí)行在1中前端定義好的functionName也就是傳進(jìn)來的callback
  5. 4中執(zhí)行的callback的參數(shù)是由后端的數(shù)據(jù)提供的,可以是字符串也可以是JSON

這個(gè)流程就是JSONP,解決跨站請(qǐng)求的方案

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容