最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

簡單易懂的JSONP和CORS跨域方案詳解

 更新時(shí)間:2022年10月11日 11:08:29   作者:mouche  
這篇文章主要為大家介紹了簡單易懂的JSONP和CORS跨域方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、了解跨域

??何為跨域

  • 跨域并不是請求發(fā)不出去,請求能發(fā)出去,服務(wù)端能收到請求并正常返回結(jié)果,只是結(jié)果被瀏覽器攔截

那為什么攔截呢,我們應(yīng)該先了解一下什么是同源策略

  • MDN上的解釋:同源策略是一個(gè)重要的安全策略,它用于限制一個(gè)origin的文檔或者它加載的腳本如何能與另一個(gè)源的資源進(jìn)行交互。它能幫助阻隔惡意文檔,減少可能被攻擊的媒介

具體的限制(三限制):

  • 同源策略限制了來自不同源的 JavaScript 腳本對當(dāng)前DOM對象讀和寫的操作
  • 同源策略限制了來自不同源的站點(diǎn)讀取當(dāng)前的 Cookie、LocalStorage 等數(shù)據(jù)
  • 同源策略限制了通過 XMLHttpRequest等方式將站點(diǎn)的數(shù)據(jù)發(fā)送給不同源的站點(diǎn)

能使的標(biāo)簽(能跨域請求資源的標(biāo)簽):

  • <script src="..."></script>
  • <link rel="stylesheet" href="...">
  • 通過 <img>展示圖片
  • 通過 <video>和 <audio>播放的多媒體資源
  • 通過 <iframe>載入的任何資源

那你限制了我這么多東西,我要正常使用肯定要跨域嘍

??何為同源

如果兩個(gè) URL 的協(xié)議(Protocol),域名(domain),端口(port)都相同的話,則這兩個(gè) URL 是同源

  • 協(xié)議:定義了數(shù)據(jù)如何在計(jì)算機(jī)內(nèi)和之間進(jìn)行交換的規(guī)則的系統(tǒng)。例如:http,https
  • 域名:可以通過DNS解析為IP地址 (這里即使兩個(gè)不同的域名指向同一個(gè)IP地址,也是屬于不同源的)
  • 端口:http默認(rèn)使用80端口,https默認(rèn)使用443端口

舉個(gè)例子: 我們尋找跟掘金首頁:https://jbzj.cn/同源的URL

URL結(jié)果原因
http://jbzj.cn/不同源協(xié)議不同
https://jbzj.my.cn/不同源域名不同
https://jbzj.cn:3030/不同源端口不一樣(https默認(rèn)443端口)
https://jbzj.cn/user/2375390426313406同源只有路徑不一樣
https://jbzj.cn/post/7151609034699718692同源只有路徑不一樣

小結(jié):跨域的出現(xiàn)是因?yàn)闉g覽器的同源策略對不同協(xié)議,域名,端口的URL在DOM對象的讀寫,Cookie等數(shù)據(jù)的獲取,Ajax請求等方面有限制

二、跨域解決方案

JSONP(JSON with Padding)

  • 最容易記住也最容易理解的應(yīng)該就是JSONP了
  • 我們前面提到了<script src="..."></script>是可以跨域請求資源的,那么JSONP就是利用了這一點(diǎn)了

??簡單實(shí)現(xiàn)

  • 先上一個(gè)簡單的例子來理解思路
  • 在客戶端
  <script>
    function jsonp(){
      return new Promise((resolve, reject) => {
      //在全局定義一個(gè)doSomething的函數(shù)
        window['doSomething'] = function(data) {
          resolve(data); 
        }
      })
    }
    jsonp().then(res=>{
    //調(diào)用doSomething之后會在這里打印
      console.log(res)
    })
  </script>
  //使用script標(biāo)簽向后臺/test路徑發(fā)送參數(shù)callback為doSomething
  <script src="http://localhost:3000/test?callback=doSomething"></script>
  • 在服務(wù)端
app.get('/test',(req, res) => {
    //通過res.send()方法-將處理好的內(nèi)容,發(fā)送給客戶端
    let { callback } = req.query;
    //返回一個(gè)函數(shù)的執(zhí)行式doSomething(‘參數(shù)')
    res.send(`${callback}('這里是某車在使用簡單的JSONP')`)
})
  • 服務(wù)器返回doSomething('這里是某車在使用簡單的JSONP'),因?yàn)槲覀円呀?jīng)聲明過,所以客戶端會直接調(diào)用doSomething這個(gè)函數(shù),然后打印出了:這里是某車在使用簡單的JSONP

??流程圖

  • 全局聲明一個(gè)回調(diào)函數(shù)
  • 在<script>標(biāo)簽中,輸入U(xiǎn)RL,向服務(wù)器傳遞該函數(shù)名和參數(shù)(可以通過問號傳參:?callback=doSomething)
  • 服務(wù)器接收到請求后,需要進(jìn)行處理:把傳遞進(jìn)來的函數(shù)名和要返回的數(shù)據(jù)拼接為函數(shù)調(diào)用式,例如:傳遞進(jìn)去的函數(shù)名是doSomething,要傳回的數(shù)據(jù)是:這里是某車在使用簡單的JSONP,那么后臺返回的即為doSomething('這里是某車在使用簡單的JSONP')
  • 客戶端再調(diào)用執(zhí)行之前聲明的回調(diào)函數(shù)(doSomething),對返回的數(shù)據(jù)進(jìn)行操作

??封裝

但是在真正的開發(fā)中,我們不可能寫一堆<script>標(biāo)簽在那里,所以我們需要對jsonp進(jìn)行封裝

    function jsonp({url, params, callback}){
      return new Promise((resolve, reject) => {
      //動(dòng)態(tài)創(chuàng)建script標(biāo)簽
        let script = document.createElement('script');
        //處理傳入的參數(shù)
        params = {...params, callback};
         //轉(zhuǎn)換參數(shù)表達(dá)式
        let arr = []
        for(let key in params) {
          arr.push(`${key}=${params[key]}`)
        }
        //在路徑中,參數(shù)用 & 隔開
        script.src = `${url}?${arr.join('&')}`
        //添加 script 標(biāo)簽
        document.body.appendChild(script);
        //聲明回調(diào)函數(shù)
        window[callback] = function(data) {
        //執(zhí)行異步函數(shù)
          resolve(data);
          //請求完后移除該script標(biāo)簽
          document.body.removeChild(script)
        }
      })
    }
  • 使用
   jsonp({
      url:'http://localhost:3000/test',
      params:{ args:'這個(gè)是參數(shù)' },
      callback: 'doSomething'
    }).then(res=>{
      console.log(res) 
    })

??優(yōu)缺點(diǎn)

  • 優(yōu)點(diǎn): 簡單兼容性好,可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題
  • 缺點(diǎn): 僅支持get方法具有局限性,不安全可能會遭受XSS攻擊

CORS(Cross-Origin Resource Sharing)

  • 實(shí)現(xiàn)CORS的關(guān)鍵在于后端:服務(wù)端設(shè)置Access-Control-Allow-Origin就可以開啟 CORS。 該屬性表示哪些域名可以訪問資源,如果設(shè)置通配符則表示所有網(wǎng)站都可以訪問資源
  • 我們可以將請求分類為簡單請求和非簡單請求

簡單請求

判定:

請求方法是以下方三種方法之一

  • HEAD
  • GET
  • POST

請求頭僅包含安全的字段,如以下幾種字段

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三個(gè)值 application/x-www-form-urlencoded、multipart/form-data、text/plain

請求過程

  • 客戶端代碼
    btn.addEventListener('click',()=>{
      const xhr = new XMLHttpRequest();
      xhr.open('get', 'http://localhost:3000/test'); //向端口號為3000的發(fā)起請求
      xhr.send();
      xhr.onreadystatechange = function(){
        if(xhr.readyState == 4) {
          if(xhr.status >= 200 && xhr.status < 300) {
            console.log(xhr.response) //如果請求成功,則打印后臺返回的數(shù)據(jù)
          }
        }
      }
    })
  • 服務(wù)端設(shè)置
app.get('/test',(req,res)=>{
    //通過res.send()方法-將處理好的內(nèi)容,發(fā)送給客戶
    res.send(`你好呀某車,你已經(jīng)成功發(fā)起請求了`)
})
  • 當(dāng)你發(fā)送請求的時(shí)候(客戶端為5500端口,服務(wù)端為3000端口),什么也不設(shè)置就會報(bào)跨域錯(cuò)誤

  • 此時(shí)我們看一下當(dāng)瀏覽器會給它的請求頭加上Origin,值為它本身的協(xié)議+域名+端口,這個(gè)時(shí)候服務(wù)器就能知道是誰在對他發(fā)送請求

  • 那么服務(wù)器如果想要允許它訪問的話,則在響應(yīng)頭加上Access-Control-Allow-Origin字段,值可以為*表示允許所有源的訪問,也可以為具體的源,我們這里給他設(shè)置為我們上述的Origin值,同時(shí)也加上Access-Control-Allow-Methods字段,設(shè)置可訪問的請求方法
app.all("*",function(req,res,next){
    //設(shè)置允許跨域的域名,*代表允許任意域名跨域
    res.header("Access-Control-Allow-Origin","http://127.0.0.1:5500");
    res.header("Access-Control-Allow-Methods",'PUT,POST,GET,DELETE,OPTIONS')
    next();
})
  • 此時(shí)你再點(diǎn)擊發(fā)送請求就會發(fā)現(xiàn):請求已經(jīng)能夠正常返回了

  • 響應(yīng)頭顯示的Access-Control-Allow-Origin即為我們可訪問的源

復(fù)雜請求

  • 我們上述已經(jīng)講過什么是簡單請求了,那么不符合上述條件的即為復(fù)雜請求
  • 復(fù)雜請求發(fā)起請求的時(shí)候則是會在正式通信之前進(jìn)行一次預(yù)檢請求(preflight request)
  • 瀏覽器先詢問服務(wù)器,當(dāng)前源當(dāng)前請求是否可以訪問服務(wù)器資源,只有得到正確的答復(fù),才會進(jìn)行正式的請求

請求過程

我們依舊使用上面的代碼,然后就請求方式改為put,此時(shí)發(fā)送的即為復(fù)雜請求,可以看到發(fā)送了兩次請求,由于我們put方法在上述已經(jīng)被設(shè)置為可訪問的方法,所以現(xiàn)在預(yù)檢可以通過,我們也能正常請求到資源

如果我們將put方法從Access-Cntrol-Allow-Methods移除,此時(shí)再進(jìn)行請求,則發(fā)提示,在預(yù)檢請求中發(fā)送PUT方法是不被允許的

此時(shí)test正式請求則報(bào)CORS錯(cuò)誤

寫到這里,我就想到了曾經(jīng)看過的面試題:跨域請求如何攜帶cookie

擴(kuò)展

  • 但是我想測試的時(shí)候受到了SameSite的限制,不允許第三方cookie,本來想要關(guān)掉瀏覽器設(shè)置的,搜索了一下發(fā)現(xiàn)這個(gè)方法自從版本91之后就用不了,那么淺淺說一下方法就得了
  • 只需要前端在發(fā)送Ajax請求的時(shí)候?qū)ithCredentials設(shè)置為true
    const xhr = new XMLHttpRequest();
    xhr.withCredentials = true;
  • 后端設(shè)置"Access-Control-Allow-Credentials為true,同時(shí)注意Access-Control-Allow-Origin值不為*
    res.header("Access-Control-Allow-Credentials", "true");

缺點(diǎn)

IE10以下不支持

小結(jié):后端是關(guān)鍵,主要是設(shè)置Access-Control-Allow-Origin

關(guān)于postMessage和webSocket的跨域方案我寫在嗦嗦postMessage和webSocket這里了

以上就是簡單易懂的JSONP和CORS跨域方案詳解的詳細(xì)內(nèi)容,更多關(guān)于JSONP CORS跨域方案的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論

田阳县| 大渡口区| SHOW| 龙江县| 南平市| 盐源县| 扎鲁特旗| 永宁县| 和龙市| 津南区| 烟台市| 东台市| 花垣县| 宜宾市| 健康| 屏东市| 泰宁县| 高碑店市| 定远县| 碌曲县| 双城市| 古丈县| 饶平县| 临夏县| 阆中市| 宜春市| 库车县| 黑水县| 许昌市| 定远县| 五指山市| 五原县| 普安县| 尉氏县| 监利县| 临汾市| 和平县| 萍乡市| 祁门县| 通许县| 郯城县|