本次分享由趣頭條cpc商業化技術部(周志祥-混元霹靂手)進行分享!歡迎大家投遞簡歷加入趣頭條,郵箱地址為qianjiongli@qutoutiao.net 期待您的加入。
你瞭解跨域嗎?
瞭解(continue) 不瞭解 (end)
為何會產生跨域?
跨域問題來源於瀏覽器同源策略的限制問題導致的。
瀏覽器為何要設定同源策略?
正是因為瀏覽器要出於安全考慮。如果缺少了同源策略,瀏覽器很容易受到XSS和CSRF等攻擊。(XSS
與CSRF
可以單獨成為一個額外的知識點) 此時會導致一個域名下網頁的操作就可以直接拿到另一個非同域名下網頁的任何資訊,或者一個網頁可以隨意請求到不同域名伺服器下的介面資料。
什麼是同源策略?
同源策略是一種約定,這是瀏覽器核心的安全功能點之一。所謂的同源策略指的是【協議 + 域名 + 埠】三者相同,如果兩個相同的域名指向同一個ip
地址,也是非同源的情況。同時地址印射對應的ip
兩者也是非同源情況。
同源策略會存在那些限制?
DOM節點
對於DOM
節點只能操作當前域名下網頁開啟的DOM
節點內容。
儲存資訊
對於cookie
、sessionStorage
、localStorage
、 indexedDB
等儲存資訊也不能非同源獲取
ajax請求
對於ajax網路請求時,請求處於非同域的情況下會被瀏覽器自動攔截報錯。
舉幾個造成跨域的場景的例子?
前面說過當協議、域名、埠號中任意一個不相同時,都是跨域。同樣包括(一級域名與二級域名的不同) 互相請求資源的情況下是一種跨域狀態。
跨域的地址場景圖
通過什麼方式可以解決跨域?
可以通過JSONP的原理
首先明白對於瀏覽器載入資源時可以通過:
- img
- script
- link
以上幾個標籤是允許跨域載入資源的。意思就是在www.baidu.com
域名下靜態html
檔案中的script
標籤可以載入wwww.google.com
伺服器下的指令碼資源等。
通過以上標籤可以載入跨域資源的理解,那我們可以通過包裝手段從其它域獲取到期望的資料。
講講JSONP的實現原理?
之前已經有了原理的思路的鋪墊。那就利用script
標籤這一允許跨域加資源的特性包裝資料進行講解。
實現流程
// index.html
// jsonp的實現模擬
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback }
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}
// 呼叫方式
jsonp({
url: 'http://localhost:3000/getUser',
params: { name: 'peter' },
callback: 'user'
}).then(data => {
console.log(data)
})
複製程式碼
通過以上程式碼實現了一個基本的JSONP
的呼叫執行程式碼。
-
宣告一個
JSONP
的模擬函式, 傳入的引數分別為請求地址、請求引數、前後端約定的包裝函式名、 內部通過返回promise
機制來優雅的解決資料返回的獲取方式。 -
通過
script
不存在跨域請求資源的機制建立一個script
臨時標籤。把向後臺請求的地址和引數組合成query
引數的形式。 請求地址:http://localhost:3000/getUser?name=peter&callback=user
關健點是把包裝的函式名(key
作為callback
, value
作為user
) 包裝函式名是前後端一個約定。
- 最後組裝後的
script
標籤插入到document
文件中,此時瀏覽器就會自動向標地址發起請求。
後臺返回的結果原理
// app.js 用express腳手架模擬的配合前臺callback封裝的返回結果
app.get('/getUser', function(req, res, next) {
let { name, callback } = req.query
console.log(name) // peter
console.log(callback) // user
res.send(`${callback}({
code: 0,
msg: '請求成功',
data: {
id: 1234
}
})`)
});
複製程式碼
後臺會通過query
引數進行解析。如果此時返回的結果是一個物件,物件中存在msg
訊息,請求狀態碼code
,資料資訊data
。
可能你會疑問為什麼返回的結果的值是放在一個user執行函式中。這就是JSONP
的核心原理。回頭再看看這段沒有解釋的程式碼段:
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
複製程式碼
當執行自己封裝的jsonp
的方法的時候在全域性定義一個函式。此函式名則是前端與後端約定的函式封裝名。當後臺返回結果時會執行約定好的全域性函式。就是執行上方程式碼段, 資料引數會通過resolve
執行返回。最後刪除對應的請求script
標籤。
JSONP和AJAX對比,區別點在那裡?
相同點:
JSONP
與ajax
兩者相同點都是客戶端向服務端發起請求。
不同點:
JSONP
屬於利用script
標籤進行了非同源策略請求,而ajax
是同源策略請求。
JSONP優缺點
優點:
JSONP
的優點是相容性很好。因為利用的是script
標籤可以非同源請求機制。這是每個瀏覽器基礎特性。
缺點:
只支援query
引數的這種get
請求方式,互動方式存在侷限性。也容易受到xss
的攻擊。
如果後臺不支援JSONP的封裝方式怎麼辦?
可以通過CORS網路通訊技術。(全稱Cross-Orgin Resource Sharing),對於CORS
同樣也需要前後端進行一個配合。但是關健點在於後臺的配置。可能你會認為。即然是後臺進行配置,為什麼前臺也需要充分的瞭解。因為無論在生產還是開發的模式下, 跨域首先對前端的影響面是最大的, 只有充分的瞭解才能向後臺去表達後臺才能準確的設定和進行配合。
簡單的跨域請求需要建議後臺進行什麼設定?
前臺模擬設定
先本地建立一個index.html
寫入請求指令碼。通過http-server -p 4000
啟動在本地4000
埠下。
// index.html
let url = 'http://localhost:3000/getUser';
let xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.send();
複製程式碼
後臺模擬設定
通過express
框架設定請求地址,服務啟動在本地3000
埠下。
// app.js
let express = require('express')
let app = express()
app.get('/getUser', function(req, res) {
res.send({
code: 0,
msg: '請求成功',
data: {
id: 1234
}
})
})
app.listen(3000)
複製程式碼
瀏覽器返回結果
訪問http://127.0.0.1:4000/index.html
可以通過Network
控制檯可以看到瀏覽器端向後臺http://localhost:3000/getUser
服務介面地址發出請求。
如果Origin
指定的源,不在許可範圍內,伺服器會返回一個正常的HTTP
回應。瀏覽器發現,這個回應的頭資訊沒有包含Access-Control-Allow-Origin
欄位,就知道出錯了,從而丟擲一個錯誤,被XMLHttpRequest
的onerror
回撥函式捕獲。注意,這種錯誤無法通過狀態碼識別,因為HTTP
回應的狀態碼有可能是200
。雖然返回的 Status Code
狀態碼是 200 OK
,但是response
響應頭裡並沒有返回期望的值。同樣在console
控制檯可以發現:
Access to XMLHttpRequest at 'http://localhost:3000/getUser' from origin 'http://127.0.0.1:4000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
複製程式碼
CORS
策略阻止了從http://127.0.0.1:4000
訪問http://localhost:3000/getuser
處的XMLHttpRequest
:請求的資源上沒有'Access- control - allow-origin'
頭。
這就是一個最簡單的CORS
的安全策略,從報錯可以很明顯的明白你需要告訴後臺需要設定'Access- control-allow-origin'
頭。
後臺解決方案
// app.js中新增
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*')
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
next()
})
複製程式碼
在接收到請求時做一層中介軟體的過濾, 以下兩者方式皆可。
- 返回時設定響應頭的
Access-Control-Allow-Origin
為*
(代表所有域名向當前服務請求都允許跨域訪問) - 返回時設定響應頭的
Access-Control-Allow-Origin
為指定的域名。其它域名都不允許進行一個跨域訪問
設定Access-Control-Allow-Origin頭就可以解決了所有的跨域問題了麻?
Access-Control-Allow-Origin
頭的設定僅僅只能解決簡單的跨域請求
簡單的跨域請求條件:
條件1: 只能允許以下的請求方法
- GET
- HEAD
- POST
條件2: Content-Type
允許條件
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
條件3: 不能超過http
的頭資訊以下欄位
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
那其它請求方式如何解決?屬於什麼型別的跨域請求?
其它的請求方式被稱之為複雜的跨域請求。一旦不符合簡單跨域請求策略的時候那就是複雜的跨域請求:
複雜的跨域請求解釋:
- 除了簡單的跨域請求的方法。比如
PUT
、DELETE
- 除了簡單的跨域請求的
Content-type
型別。比如application/json
- 自定義的
header
頭 - 不同域名下的
cookie
傳輸
嘗試解決複雜跨域的幾種情況
1.put、delete等請求方法造成複雜請求
// 修改請求方法
- xhr.open('get', url, true);
+ xhr.open('put', url, true);
複製程式碼
// 修改後臺接收請求方法
app.put('/getUser') // 省略... 對於後臺只是把get請求換成put接收請求
複製程式碼
在瀏覽器的netWork
中發現並沒有傳送put
請求,在General
中的Request Method
發現傳送了一個OPIONS
的預檢請求(關於預檢後續會在解決跨域問題中通過關閉瀏覽器策略中專門介紹相關詳細知識點)
同時瀏覽器中會被髮出報錯資訊:
Access to XMLHttpRequest at 'http://localhost:3000/getUser' from origin 'http://127.0.0.1:4000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
複製程式碼
解決方案:
// 在app.use中新增新的設定頭
// res.setHeader('Access-Control-Allow-Methods', '*')
res.setHeader('Access-Control-Allow-Methods', 'PUT')
複製程式碼
以上設定了接收允許那些請求方法:
- 設定
*
, 表示所有請求方法都允許。 - 設定對應的請求方法以逗號分隔。
2.content-type造成複雜請求
+ xhr.setRequestHeader('content-type', 'application/json');
複製程式碼
在之前談論簡單跨域請求條件二, 關於content-type
型別對於簡單的跨域請求只支援三種。設定其它的則會產生複雜的跨域請求。當設定content-type: application/json
的情況下,同樣的瀏覽器會發出報錯資訊:
Access to XMLHttpRequest at 'http://localhost:3000/getUser' from origin 'http://127.0.0.1:4000' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.
複製程式碼
從報錯提示可以看出後臺需要對複雜跨域請求content-type
進行一個額外的設定:
// 在app.use中新增新的設定頭
+ res.setHeader('Access-Control-Allow-Headers', 'content-type')
複製程式碼
3.自定義頭造成複雜請求
+ xhr.setRequestHeader('X-Customer-Header', 'value');
複製程式碼
在之前談論簡單跨域請求條件三中, 除了以上幾種http
請求頭之後,都屬於自定義頭。在請求帶入時會造成複雜的跨域請求, 同樣的瀏覽器會發出報錯資訊。
Access to XMLHttpRequest at 'http://localhost:3000/getUser' from origin 'http://127.0.0.1:4000' has been blocked by CORS policy: Request header field x-customer-header is not allowed by Access-Control-Allow-Headers in preflight response.
複製程式碼
同樣的原理對於前臺設定的自定義頭後,後臺在接收的時候同樣也要進行允許設定接收前臺自定義傳輸出來的自定義頭。
res.setHeader('Access-Control-Allow-Headers', 'content-type, X-Customer-Header')
// res.setHeader('Access-Control-Allow-Headers', '*')
複製程式碼
在Access-Control-Allow-Headers
設定的時候,可以用逗號分隔,進行多個自定義頭的設定。同時也可以傳入*
,允許所任何自定義頭。
談談CROS中的cookie?
絕對同域的情況下
在絕對同域的情況下。前臺向後臺請求的介面或者請求檔案的時候,會自動把cookie
帶入請求頭中。
在非同域的情況下
在非同域的情況下。需要使用CORS
的策略進行傳輸。預設情況下,cookie
並不會帶入請求頭中,需要對xhr
設定請求憑證。
xhr.withCredentials = true
複製程式碼
簡單的跨域請求與cookie
如果此時是簡單的跨域請求, 設定withCredentials = true
的情況下。請求頭中會帶入cookie
資訊, 後臺接收請求並且會傳送到前臺, 此時瀏覽器端從response
中可以看到資料已經返回,但是並不能獲取的後臺返回的資料, 因為此時會被xhr
的錯誤進行捕獲,瀏覽器控制檯會出現以下提示:
Access to XMLHttpRequest at 'http://localhost:3000/getUser' from origin 'http://localhost:4000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Credentials' header in the response is '' which must be 'true' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
複製程式碼
複雜的跨域請求
如果此時是複雜的跨域請求,設定withCredentials = true
的情況下。此時會傳送一個OPTIONS
請求。瀏覽器發出的錯誤資訊仍然是與簡單的跨域請求報錯一致。
解決方案
此時前臺傳送cookie
憑證, 同樣的後臺一樣需要同意接收憑證。
res.setHeader('Access-Control-Allow-Credentials', true)
複製程式碼
反向原理:
如果後臺同意接收憑證。而前臺沒有設定傳送憑證的情況下。就算後臺傳送到前臺的響應頭中設定了cookie
資訊(set-cookie頭),無論是簡單的跨域請求還是複雜的跨域請求都會導致cookie
塞入無效,可以檢視appliation/cookie
中, 不會有後臺寫入的cookie
資訊。
保持同源策略
為了安全問題。cookie
本質上還是保持了同源策略的模式。在前後臺都設定了傳送/接收憑證之後, 對於反回的origin
頭的設定res.setHeader('Access-Control-Allow-Origin', '*')
不能為*
, 需要設定成指定請求的來源 res.setHeader('Access-Control-Allow-Origin', req.headers.origin)
。
合法組合與非法組合。
當設定Credentials
的時候,後臺需要知道Access-Control-Allow
的合法與非法組合性。
一旦Access-Control-Allow-Credentials
設定為true
的時候, 此時以下幾個不能設定為*
, 需要進行指定, 否則以下三者一率視為無效設定。
- Access-Control-Allow-Headers
- Access-Control-Allow-Origin
- Access-Control-Allow-Methods
CORS情況下如何在xhr中拿到響應頭中的資訊?
可以通過xhr.getResponseHeader
方法進行獲取。但是此方法只能拿到6個基本欄位:Cache-Control
、Content-Language
、Content-Type
、Expires
、Last-Modified
、Pragma
。
在後臺響應的時候可以響應頭中塞入一些自定義的頭和值。
res.setHeader('name', 'peter')
複製程式碼
在響應體的報文中可以看到:
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: content-type, X-Customer-Header
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Origin: http://localhost:4000
Connection: keep-alive
Content-Length: 50
Content-Type: application/json; charset=utf-8
Date: Sun, 17 Feb 2019 08:18:08 GMT
ETag: W/"32-oUKytSTXnBL0hnySFj9PpHgmBQk"
name: peter // 重點在這裡
X-Powered-By: Express
複製程式碼
通過報文可以發現返回的很多之前後臺設定的資訊和這裡最關健的name
頭資訊。但是通過以下方法測試之後結論:
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.getResponseHeader('Content-Type'))
console.log(xhr.getResponseHeader('name'))
}
}
}
複製程式碼
在xhr
返回成功之後。分別獲取兩個頭資訊。
-
Content-Type
則會返回application/json; charset=utf-8
-
name
則會提示報錯資訊,並且返回null
空值。
Refused to get unsafe header "name" // 拒絕獲取不安全的頭資訊“name”
複製程式碼
可以明確的知識,除了之前提到的以上六種頭資訊可以進行獲取之外,其餘的一律都需要在後臺進行允許那響應些頭訪問的設定。
res.setHeader('Access-Control-Expose-Headers', 'name')
複製程式碼
此時瀏覽器中報錯資訊不會存在,同時也能列印出name
在響應頭中的值。注意 如果設定的值為 *
則無效。需要對指定欄位頭進行設定。
複雜的跨域請求會造成每次請求都傳送一個OPTIONS請求,如何解決?
通過以上的所有對複雜的跨域請求的分析清楚的認識到,那些請求方式會造成傳送預檢,一句話概括,**Access-Control-Max-Age 這個響應首部表示 preflight request (預檢請求)的返回結果(即 Access-Control-Allow-Methods 和Access-Control-Allow-Headers 提供的資訊) 可以被快取多久。**這樣對network
中的請求觀察和請求效能來說都不友好。如果做到友好又安全的機制。
對預檢進行一個時間請求有效期
res.setHeader('Access-Control-Max-Age', 600)
複製程式碼
對預檢請求設定10
分鐘的過期時間(時間可以根據專案情況進行自定義)。但是對於每個瀏覽器的快取時間機制都不一樣。在本地除錯的時候,有時候你會發現設定了預檢的過期時間並不生效。注意一下可能開啟了瀏覽器的Disable cache導致了此原因
在前後端聯調時,不通過後端設定,如何解決跨域問題?
關閉瀏覽器跨域策略。
通過之前分析整個跨域模式是由前臺瀏覽器的所作所為造成的。為了安全,瀏覽器對跨域請求做了一系列的驗證。那是否可以想想, 通過手動關閉瀏覽器跨域策略是不是可以解決根本性的問題。
Mac 建立一個chrome.sh檔案
#!/bin/bash
#!/bin/sh
open -a "Google Chrome" --args --disable-web-security --user-data-dir
exit 0
複製程式碼
通過終端執行:
sh 加上chrome.sh檔案地址
複製程式碼
注意:
在執行終端命令的時候,先檢查是否已經啟動過chrome
,如果啟動過需要手動關閉整個chrome
的程式。
成功結果:
輸入URL
地址之後。所有的跨域問題會一併解決。
原理
雖然瀏覽器的跨域策略已經被關閉了。不存在任何瀏覽傳送的跨域行為, 其內部原理正是因為瀏覽器會對簡單的跨域請求做了攔截和複雜的跨域請求做了傳送預檢。
簡單的跨域請求通過什麼進行攔截?
在理解簡單的跨域請求時先需要理解兩個請求頭的欄位。
request請求頭中的Origin
請求首部欄位 Origin
指示了請求來自於哪個站點。該欄位僅指示伺服器名稱,並不包含任何路徑資訊。該首部用於 CORS
請求。
通俗的說就是告訴伺服器此時是從那個域名地址傳送來的。只有在CORS
的情況下Origin
才會在請求頭中出現。
request請求頭中的HOST
Host
請求頭指明瞭伺服器的域名(對於虛擬主機來說),以及(可選的)伺服器監聽的TCP埠號。
如果沒有給定埠號,會自動使用被請求服務的預設埠(比如請求一個HTTP
的URL
會自動使用80
埠)。
HTTP/1.1
的所有請求報文中必須包含一個Host頭欄位。如果一個 HTTP/1.1
請求缺少Host
頭欄位或者設定了超過一個的 Host
頭欄位,一個400(Bad Request
)狀態碼會被返回。
通俗的說就是瀏覽器向服務端傳送請求時, 所請求的伺服器的域名地址。
響應頭中的Access-Control-Allow-Origin
響應頭指定了該響應的資源是否被允許與前臺請求頭給定的origin
共享。
結論
所以跨域請求返回瀏覽器之後。雖然資料會返回但是。瀏覽器會比對請求頭中的Origin
與響應頭中的Access-Control-Allow-Origin
是否是共享匹配,如果不匹配。瀏覽器的xhr
會捕獲錯誤並且在瀏覽器端控制檯丟擲錯誤。並不能拿到期望的資料。
複雜的請求瀏覽器是如何檢測跨域的?
對於複雜的請求跨域, 瀏覽器一旦檢測此傳送的請求頭存在屬於複雜的跨域請求時, 首先會傳送一個預請求, 請求頭中包函著以下重要的內容:
Access-Control-Request-Headers
(如果有自定義頭或者content-type
類形不屬於簡單請求的型別的情況下才會出來)Access-Control-Request-Method
(除了簡單的請求方法才會出現)
並且在傳送預檢請求時並不會把請求資料和cookie
資訊帶入請求資訊中。
什麼是預檢請求?
在CORS
中會使用 OPTIONS
方法發起一個預檢請求(preflight request), 以獲知伺服器是否允許該實際請求。"預檢請求“的使用,可以避免跨域請求對伺服器的使用者資料產生未預期的影響。
當瀏覽器請求頭中發出request-Header
或者request-Method
時。此時服務端需要同意這兩個請求頭中對應的資訊通過允許。需要在響應返回的時候對響應頭做出響應處理。需要對Access-Control-Allow-Methods
和Access-Control-Allow-Headers
設定。
原理圖:
附帶Credentials
(身份憑證的)請求屬於簡單的跨域請求還是複雜的跨域請求?
關於Credentials
在CORS
中原理性已經講的很明白了。但是這裡想講的就是在xhr
中Credentials
設定為true
時。此時只是簡單的跨域請求,不會傳送預檢(OPTIONS
)請求, 如果此時是複雜的跨域請求。會傳送預檢(OPTIONS
)請求。
所以Credentials
是否會傳送預檢,主要需要通過其它請求頭的判定來決定是否需要傳送預檢。
原理圖:
總結:
只有當request
請求頭與response
返回頭一一對應上了。互相允許通過共享策略。對於簡單的跨域請求則不會被捕獲錯誤.對於複雜的跨域請求則會傳送真正的請求。同時會把cookie
等傳輸資料帶入請求體中。所以說關閉瀏覽器跨域策略就是關閉了瀏覽器對響應Origin
頭匹配時不再捕獲,同時也會關閉對應的OPTIONS
預檢請求。直接傳送給對應的後臺伺服器。所以說本質上雖然存在跨域,但是服務端永遠是返回資料。一切的錯誤或者沒有傳送真正的請求都是瀏覽器的安全機制所為。
如何通過代理劫持機制解決跨域?
前面我們已經知道瀏覽器向伺服器請求是存在跨域問題,但是伺服器向伺服器傳送請求是不存在跨域問題。通過MS(middle server)
進行請求劫持之後,通過服務端向服務端傳送請求,再二次返回給瀏覽器端。
示意圖:
在各大框架中都通過腳手架啟動node
服務承載著專案。例如vue-cli
中就利用了http-proxy-middle
進行一個請求的代理攔截,向目標伺服器傳送請求來解決跨域問題。
// 通過express啟用3000埠
// index.html
<script>
let url = '/api/getUser';
let xhr = new XMLHttpRequest();
xhr.open('post', url, true);
xhr.setRequestHeader('content-type', 'application/json');
xhr.setRequestHeader('X-Customer-Header', 'value');
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(1)
console.log(xhr.response)
}
}
}
</script>
const proxyOption = {
target: 'http://localhost:4000',
pathRewrite: {
'^/api/' : '/' // 重寫請求,api/解析為/
},
changeOrigin:true
};
app.use('/api', proxy(proxyOption))
複製程式碼
// 後臺服務啟動4000埠
app.post('/getUser', (req, res, next) => {
res.send({
code: 1
})
})
複製程式碼
當3000
埠的靜態檔案傳送ajax
請求的時候,本身就是在一個域名下,不會造成任何跨域問題,同時會被app.use('/api/')
捕獲攔截,同時改寫url
地址向服務端4000
端進行請求傳送資料。此時就是server
端與server
端的請求通訊。當4000
埠的server
接收到請求之後把資料返回給3000
埠的server
端,同時再返回給請求的ajax
。
用node原生API如何實現?
app.use('/api', (req, res) => {
const reqHttp = http.request({
host: '127.0.0.1',
path: '/getUser',
port: '4000',
method: req.method,
headers: req.headers
}, (resHttp) => {
let body = ''
resHttp.on('data', (chunk) => {
console.log(chunk.toString())
body += chunk
});
resHttp.on('end', () => {
res.end(body)
});
})
reqHttp.end()
});
複製程式碼
以上程式碼本質上是模擬了代理劫持的方式,同時當攔截到url
開頭以/api
起始的請求之後,通過node
原生http
模組的request
方法向對應的後臺傳送請求,同時把瀏覽器請求過來的一些請求體,請求頭等資料一併傳給server
端。通過http
模組監聽的結束方法最後把資料再返回到client
瀏覽器端。這樣形成了二次轉方式解決跨域問題。整體就是利用了服務端向服務傳送請求不會有跨域策略的限制,就是所謂的同源策略。因為瀏覽器會做options
等預檢的檢測,而服務端並不會。