第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Ajax 跨域

我們先從這么一個(gè)問(wèn)題來(lái)引入我們本章節(jié)的學(xué)習(xí) —— 什么是跨域請(qǐng)求?

1.跨域請(qǐng)求

簡(jiǎn)單來(lái)說(shuō),跨域請(qǐng)求就是一個(gè)域下的資源請(qǐng)求另外一個(gè)域下的資源。

同一個(gè)域,指的是,協(xié)議名、域名、端口號(hào)都一致。 舉個(gè)例子來(lái)說(shuō),假如 “http://www.a.com” 下的 JavaScript 腳本發(fā)起 Ajax 請(qǐng)求 “http://www.a.com/ajax” ,由于 協(xié)議名 http 、域名 www.a.com 和 端口號(hào)(默認(rèn)都是 80)三者都是一致的,因此都屬于同一個(gè)域,不造成跨域請(qǐng)求。而假如其中任一元素不相同,則造成跨域請(qǐng)求。與此同時(shí),瀏覽器出于安全考慮,基于同源策略則會(huì)做一定的限制:比方說(shuō):

  • 無(wú)法獲取不同域的 Cookie、LocalStorage 等等。
  • 無(wú)法獲取不同域的 DOM 對(duì)象。
  • 無(wú)法向不同域發(fā)送 Ajax 請(qǐng)求。

2.正文須知

本章節(jié)不考慮不同域文檔之間的跨域交互,主要講 Ajax 造成的跨域的解決方法。

開(kāi)始講解 Ajax 造成的跨域問(wèn)題如何解決之前,我們思考一下:

假如我們要從山的一邊 A 到山的另一邊 B,這座山無(wú)疑就是個(gè)障礙,那么我們有幾種解決辦法?

我想,要么我們就直接穿過(guò)去,要么我們就“曲線救國(guó)”,繞個(gè)道也未嘗不可。沒(méi)錯(cuò),接下來(lái)我們要講的 Ajax 跨域也是從這兩方面來(lái)講,既然跨域有這樣那樣的一些限制,那我們要么就直面去解決,要么就耍個(gè)機(jī)靈,同樣能夠解決。

3.曲線救國(guó)法

3.1 JSONP

JSONP 是一個(gè)非常經(jīng)典的解決跨域的方法。我們知道,在 HTML 中,一些資源的引用事實(shí)上是不會(huì)受到跨域限制的,比如 script 標(biāo)簽。瀏覽器在解析 HTML 的時(shí)候,解析到了 script 標(biāo)簽,會(huì)把相應(yīng)的資源下載下來(lái)。我們可以利用這一點(diǎn),來(lái)實(shí)現(xiàn)前后端信息的交互。

3.1.1 JSONP 原理

  1. 定義好回調(diào)函數(shù),比方說(shuō)命名為 callback ,并將函數(shù)名作為 url 的參數(shù);
  2. 添加 script 標(biāo)簽,指定的資源為目標(biāo)域的方法,也就是上面的 url ;
  3. 后端接收 GET 請(qǐng)求,返回 callback(responseData) 格式數(shù)據(jù),把要返回的數(shù)據(jù) responseData 傳到 callback() 中;
  4. 前端接收 javaScript 內(nèi)容,執(zhí)行了后端返回的 callback(responseData) ,這樣就完成了一次前后端交互了。

3.1.2 具體例子

假如 HTML 有一個(gè)容器為 container,我們要通過(guò) JSONP 的方式來(lái)為 container 插入一條內(nèi)容,那么,我們可以這么做:

3.1.2.1 HTML 關(guān)鍵代碼
<div id="container">

</div>
3.1.2.2 javaScript 關(guān)鍵代碼
// jsonp

// 定義一個(gè)添加內(nèi)容的回調(diào)函數(shù)
window.addContent = function (content) {
    document.getElementById('container').innerHTML = content;
}

/**
* 發(fā)送 JSONP 請(qǐng)求的函數(shù)
* cb 為回調(diào)函數(shù)的函數(shù)名
*/
function sendJsonPRequest (cb) {
    // 創(chuàng)建 script 標(biāo)簽
    const body = document.getElementsByTagName('body')[0];
    const script = document.createElement('script');
    script.type = 'text/javascript';
    
    // 指定標(biāo)簽的 url ,callback 參數(shù)為回調(diào)函數(shù)的函數(shù)名
    script.src = `http://localhost:8082/jsonp/get?callback=${cb}`;
    body.appendChild(script); // 添加到 body 最后面
}

sendJsonPRequest('addContent') // 執(zhí)行發(fā)送 JSONP 請(qǐng)求

顯而易見(jiàn),前端我們會(huì)創(chuàng)建一個(gè) script 標(biāo)簽,并且附帶定義好的回調(diào)函數(shù)的函數(shù)名傳給服務(wù)端。與此同時(shí),我們需要在服務(wù)端進(jìn)行 JSONP 請(qǐng)求的響應(yīng)。

3.1.2.3 服務(wù)端關(guān)鍵代碼
router.get("/jsonp/get", function(req, res) {
    const cb = req.query.callback; // 讀取請(qǐng)求附帶的參數(shù) callback
    const resData = '這是一條服務(wù)端返回的內(nèi)容';
    res.send(`${cb}(${JSON.stringify(resData)})`); // 返回 callback(resData) 格式的數(shù)據(jù)
});
3.1.2.4 效果

圖片描述

從右邊控制臺(tái)可以看出來(lái),我們成功創(chuàng)建了 JSONP 的請(qǐng)求,并且結(jié)果正如我們預(yù)期的執(zhí)行了 addContent('這是一條服務(wù)端返回的內(nèi)容'),界面上展示出插入的內(nèi)容。

3.1.3 JSONP 小結(jié)

使用 JSONP 的方式,我們可以通過(guò) script 標(biāo)簽繞過(guò)瀏覽器的跨域限制,進(jìn)行前后端數(shù)據(jù)交互。不過(guò)另一方面,這種方法也很有局限性,我們只能夠發(fā)送 GET 請(qǐng)求,無(wú)法滿足更加復(fù)雜業(yè)務(wù)的需求。一般我們也不會(huì)推薦直接使用 JSONP 的方式來(lái)解決跨域問(wèn)題。

3.2 服務(wù)端代理

接下來(lái)講到的一種是服務(wù)端代理的方式。要問(wèn)為什么采取服務(wù)端代理的方式呢?很簡(jiǎn)單,因?yàn)闉g覽器端 Ajax 請(qǐng)求有跨域的限制,那我們就把請(qǐng)求不同域的操作放在服務(wù)端好了,畢竟服務(wù)端是沒(méi)有跨域限制這一說(shuō)的。

3.2.1 服務(wù)端代理原理

  1. 瀏覽器端發(fā)送請(qǐng)求到同域的服務(wù)端;
  2. 服務(wù)端接收到請(qǐng)求之后,進(jìn)行轉(zhuǎn)發(fā),請(qǐng)求不同域的另外一個(gè)服務(wù)端;
  3. 服務(wù)端間進(jìn)行交互數(shù)據(jù)后,同域服務(wù)端返回?cái)?shù)據(jù)給瀏覽器端。

3.2.2 具體例子

舉一個(gè)服務(wù)端代理的例子,這里我使用了一個(gè) Express 的中間件,叫做 express-http-proxy 。當(dāng)然同學(xué)們也可以在同域服務(wù)端接收到請(qǐng)求的時(shí)候,發(fā)起 http 請(qǐng)求訪問(wèn)不同域的服務(wù)端來(lái)模擬這一代理行為。前端方面我使用了 jQuery 的 Ajax 方法。

3.2.2.1 javaScript 關(guān)鍵代碼
$.ajax({
    url: '/proxy/proxy_get',
    method: 'GET',
    data: {
        a: '123',
        b: '234'
    }
}).done(data => {
    console.log(data)
})

很簡(jiǎn)單,我們就是向同域的服務(wù)器發(fā)送了一個(gè)請(qǐng)求。

3.2.2.2 同域服務(wù)器關(guān)鍵代碼
const proxy = require('express-http-proxy');  // 引入代理中間件

// ... 一些代碼

app.use('/proxy', proxy('http://localhost:8082/')); // 注冊(cè),之后 /proxy 都會(huì)代理到 http://localhost:8082/ 上
3.2.2.3 不同域的服務(wù)器關(guān)鍵代碼
router.get("/proxy_get", function(req, res) {
    const {a, b} = req.query
    res.send(`參數(shù)是:${a}${b}`)
});

這是目標(biāo)服務(wù)器的響應(yīng)方法,返回一個(gè) 處理后的字符串。

3.2.2.4 效果

圖片描述

3.2.3 服務(wù)端代理小結(jié)

服務(wù)端代理通過(guò)服務(wù)端和服務(wù)端之間的交互來(lái)避免瀏覽器和不同域的服務(wù)端之間直接進(jìn)行交互,從而避免了跨域的問(wèn)題。當(dāng)然這種方法要求我們有一個(gè)中間服務(wù)器的存在。

4.面對(duì)疾風(fēng)法

舉了兩個(gè)繞過(guò)跨域限制的方法,接下來(lái)我們要談?wù)劤R?guī)解決的情況。既然有跨域限制了,我們就來(lái)老老實(shí)實(shí)解決這個(gè)問(wèn)題。

接下來(lái)我要講的,是 CORS

4.1 CORS

首先展開(kāi)一下 CORS 的全稱:

Cross-origin resource sharing

意思是跨域資源共享,這是一個(gè) W3C 標(biāo)準(zhǔn),從字面意思來(lái)看不難理解,它允許瀏覽器向跨域的資源發(fā)送請(qǐng)求,并且獲得結(jié)果數(shù)據(jù)。

4.1.1 CORS 原理

跨域資源共享標(biāo)準(zhǔn)新增了一組 HTTP 首部的字段,使得我們能夠通過(guò)這些字段來(lái)跨域獲取到我們所需要的資源。而要實(shí)現(xiàn)這一功能,我們需要前后端的配合,只有當(dāng)后端實(shí)現(xiàn)了 CORS 功能,我們才能夠通過(guò)瀏覽器直接訪問(wèn)資源。為此,我們先來(lái)看看接下來(lái)的幾個(gè)首部字段:

  • Access-Control-Allow-Origin :表示服務(wù)端允許的請(qǐng)求源的域,如果是 * 表示允許所有域訪問(wèn),一般我們不建議使用 *;
  • Access-Control-Allow-Headers: 表示預(yù)檢測(cè)中,列出了將會(huì)在正式請(qǐng)求的 Access-Control-Request-Headers 字段中出現(xiàn)的首部信息;
  • Access-Control-Allow-Methods: 表示服務(wù)端允許的請(qǐng)求方法
  • Access-Control-Allow-Credentials: 表示服務(wù)端是否允許發(fā)送cookie。當(dāng)然前端也需要設(shè)置對(duì)應(yīng)的 xhr.withCredentials 來(lái)進(jìn)行配合;
  • Access-Control-Expose-Headers: 列出了可以作為響應(yīng)的一部分暴露在外的頭部信息。

其中,我們更為重要的當(dāng)屬 Access-Control-Allow-Origin 字段,因?yàn)檫@個(gè)字段直接關(guān)系到你是否能夠跨域訪問(wèn)資源的權(quán)限了。通常情況下,為了解決跨域問(wèn)題,后端同學(xué)會(huì)設(shè)置 Access-Control-Allow-Origin 指定為我們的請(qǐng)求源的域,而前端代碼基本無(wú)感。

4.1.2 簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求

關(guān)于 CORS ,HTTP 請(qǐng)求上會(huì)有一些小小區(qū)別,最直觀的區(qū)別就是會(huì)不會(huì)觸發(fā)多一次 OPTIONS 預(yù)檢測(cè)請(qǐng)求。我們把一些不會(huì)觸發(fā)預(yù)檢測(cè)請(qǐng)求的請(qǐng)求,稱為簡(jiǎn)單請(qǐng)求,而相反,會(huì)觸發(fā)預(yù)檢測(cè)的請(qǐng)求則是非簡(jiǎn)單請(qǐng)求

而關(guān)于如何區(qū)分簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求,這里我就不再累贅,有興趣的同學(xué)可以讀一下 HTTP 控制訪問(wèn) 。在實(shí)際的工作過(guò)程中,使用到 CORS 來(lái)解決跨域限制是非常常見(jiàn)的,這里我們注意一下簡(jiǎn)單請(qǐng)求和非簡(jiǎn)單請(qǐng)求的直觀區(qū)別即可,并在以后的工作中留意一下,而不至于懵逼于為什么多了一次 OPTIONS 請(qǐng)求。

4.1.3 具體例子

4.1.3.1 服務(wù)端核心代碼
// 全局設(shè)置請(qǐng)求過(guò)濾
app.all('*',function (req, res, next) {
    res.header('Access-Control-Allow-Origin', 'http://localhost:8080'); // 設(shè)置 Access-Control-Allow-Origin
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With');  // 設(shè)置 Access-Control-Allow-Headers
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS'); // 設(shè)置 Access-Control-Allow-Methods
    next()
});


// 注冊(cè)一個(gè)簡(jiǎn)單的路由
router.get("/simple/get", function(req, res) {
    const {a} = req.query
    res.send(`參數(shù)值是${a}`)
});

后端要做的工作就是實(shí)現(xiàn) CORS 功能。正如上方代碼,我們規(guī)定了一系列 HTTP 請(qǐng)求頭首部字段,使得 http://localhost:8080 這個(gè)域的前端腳本擁有向服務(wù)端發(fā)起請(qǐng)求并取得資源的權(quán)限。

4.1.3.2 前端核心代碼
$.ajax({
    url: 'http://localhost:8083/simple/get',
    method: 'GET',
    data : {
        a: 1
    }
}).done(data => {
    console.log(data)
})
4.1.3.3 效果

圖片描述

可見(jiàn),通過(guò) CORS ,前端成功拿到了不同域的服務(wù)端的返回內(nèi)容。

4.1.4 CORS 小結(jié)

CORS 是一個(gè) W3C 的標(biāo)準(zhǔn)。使用 CORS ,我們可以使用使用常規(guī)的方式來(lái)解決前后端跨域訪問(wèn)的問(wèn)題。并且,大多數(shù)的工作其實(shí)也是放在了服務(wù)端上,對(duì)于前端而言,基本上可以說(shuō)是無(wú)感的。

當(dāng)然, CORS 也是存在著一些弊端。正因?yàn)樗?W3C 中一個(gè)比較新的方案,導(dǎo)致了各大瀏覽器引擎沒(méi)有對(duì)其做嚴(yán)格規(guī)格的實(shí)現(xiàn),由此可能產(chǎn)生一些不一致的情況。

5.本章最后

跨域遠(yuǎn)不止 Ajax 跨域,而解決 Ajax 跨域的方法也不只有本章中提到的這三種。

說(shuō)跨域遠(yuǎn)不止 Ajax 跨域,打個(gè)比方,不同域的網(wǎng)頁(yè)之間的通信也是屬于跨域范疇。但由于本章的主題是 Ajax 跨域,因此我們不做過(guò)多的討論。有興趣的同學(xué),可以深入去探究一下。

而解決 Ajax 跨域的方法,本章提及 3 種方法,從兩個(gè)方面來(lái)闡述。對(duì)于遇見(jiàn)的問(wèn)題,解決的方法要么就是繞個(gè)道走,要么就是穿過(guò)去走。無(wú)論你使用哪一種方法,肯定也都有利有弊。而實(shí)際的應(yīng)用中,我們到底要采用何種方法來(lái)解決 Ajax 跨域問(wèn)題呢?我的建議是關(guān)注業(yè)務(wù)和場(chǎng)景,這就需要同學(xué)們?cè)诹硗庖粋€(gè)層面去進(jìn)行深入的思考了。

本著鼓勵(lì)深入學(xué)習(xí)深入思考的原則,我希望同學(xué)們能夠在跨域的問(wèn)題上,進(jìn)行深入的研究,總結(jié)起來(lái)。