瀏覽器中的HTTP請求
XMLHttpRequest
XHR對象用於與服務器交互。通過XMLHttpRequest可以在不刷新頁面的情況下請求特定URL,獲取數據。
XMLHttpRequest在AJAX編程中被大量使用。
Fetch
Fetch API提供了一個獲取資源的接口(包括跨域請求)
AJAX
Asynchronous JavaScript And XML,是一種使用XMLHttpRequest技術構建更復雜,動態的網頁的編程實踐。大部分的ajax其實就是對XMLHttpRequest的相關API進行封裝,使其使用起來更加方便。
跨域
跨域,顧名思義,跨越區域。大概意思為訪問的網站請求非同源資源。
| 當前頁面URL | 被請求資源URL | 跨域 | 原因 |
|---|---|---|---|
| http://www.test.com | http://www.test.com/api/users | 否 | 同源(協議 域名 端口號相同) |
| http://www.test.com | https://www.test.com/api/users | 是 | 協議不同(http/https) |
| http://www.test.com | http://www.baidu.com/api/users | 是 | 主域名不同(test/baidu) |
| http://www.test.com | http://blog.test.com/api/users | 是 | 子域名不同(www/blog) |
| http://www.test.com:8080 | http://www.test.com:7070/api/... | 是 | 端口號不同(8080/7070) |
為什麼會有跨域
遇事先問為什麼。所以,瀏覽器為什麼要設置跨域的限制,然後我們後面還要費心費力地消除跨域的限制。
為了web生態的安全。看一個例子:
假設瀏覽器裏面的代碼可以隨意訪問第三方的數據(非同源),那麼你可以讓你的代碼定時輪詢訪問一個非同源的網頁,假設某個時刻恰好有10萬人在訪問你的網頁,那這個第三方的網頁每秒就要承受10萬的併發量,這樣網絡中大量的帶寬就會被這樣白白的浪費掉,整個web生態將會混亂無比。
當訪問的網站需要請求非同源資源時,瀏覽器將拒絕這些非同源請求。在這種情況下,我們需要解決瀏覽器跨域時拒絕請求非同源資源的限制。
當瀏覽器出現跨域時,那就不可避免的引出兩個關鍵的概念了。簡單請求和非簡單請求。
當跨域產生時,非簡單請求會在真正向服務端發送請求前進行預檢請求(OPTIONS),。
簡單請求
1、條件定義:若請求滿足以下所有的條件,則請求可視為簡單請求。
- 使用下列方法之一:
- GET
- HEAD
- POST
- 請求首部字段不得超出以下集合
- Accept
- Accept-Language
- Content-Language
- Conent-Type:text/plain || multipart/form-data || application/x-www-form-urlencoded
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- 請求中的任意XMLHttpRequestUpload 對象均沒有註冊任何事件監聽器
- 請求中沒有使用 ReadableStream 對象
非簡單請求
1、條件定義:若請求滿足下列任一條件時,即應首先發送預檢請求(options)。
- 使用了下面的任一方法:
- PUT
- DELETE
- CONNECT
- OPTIONS
- TRACE
- PATCH
- 設置了額外的請求首部字段(除去以下集合中的)
- Accept
- Accept-Language
- Content-Language
- Conent-Type:text/plain || multipart/form-data || application/x-www-form-urlencoded
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- 請求中的XMLHttpRequestUpload 對象註冊了任意多個事件監聽器
- 請求中使用了ReadableStream對象
解決跨域的方案
jsonp
JSON with Padding,是JSON的一種使用模式,可以讓網頁從別的網域獲取資料。由於同源策略,一般來説位於server1.example.com的網頁無法與不是server1.example.com的服務器溝通,而HTML的<script>元素是一個例外。利用<script>元素的這個開放策略,網頁就可以實現跨域獲取後端接口數據。
由於使用script標籤的src屬性,因此只支持get方法。
當使用JSONP這種方案時,前後端都要有相對應的寫法。
大致流程就是,前端通過<script>標籤的src屬性向後台接口發起請求(只支持GET請求),並且傳遞參數callback='response',與此同時,前端必須定義函數response(responseData),這是用來處理接口返回數據後一些操作。
當接口收到請求,返回數據格式為response(responseData)。這樣,當前端接受到數據response(responseData),就剛好執行了我們已經定義好的response(...)
當報錯如下時:
原因是:後端接口沒有返回callback(...)
CORS
Cross Origin Resource Sharing,跨域資源共享,由一系列傳輸的HTTP頭組成,這些HTTP頭決定瀏覽器是否阻止前端JavaScript代碼獲取跨域請求的響應。
1、Access-Control-Allow-Origin:指示請求的資源能共享給哪些域
2、Access-Control-Allow-Credentials:指示當請求的憑證標記為true時,是否響應該請求
3、Access-Control-Allow-Headers:用在對預請求的響應中,哪些HTTP方法允許訪問請求的資源
4、Access-Control-Expose-Headers:指示哪些HTTP頭的名稱能在響應中列出
5、Access-Control-Max-Age:指示預請求的結果能被緩存多久
6、Access-Control-Request-Headers:用於發起一個預請求,告知服務器正式請求會使用哪些HTTP頭
7、Access-Control-Request-Method:用於發起一個預請求,告知服務器正式請求會使用哪一種HTTP請求方法
8、Origin:指示獲取資源的請求是從什麼域發起的
koa2中接口允許跨域響應,響應頭部字段設置如下:
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, DELETE, PUT');
ctx.set('Access-Control-Allow-Headers', 'X-Requested-With, User-Agent, Referer, Content-Type, Cache-Control,accesstoken');
ctx.set('Access-Control-Max-Age', '86400');
ctx.set('Access-Control-Allow-Credentials', 'true');
注意事項:
若添加了自定義的Header字段,必須將這個字段名添加到服務端響應頭部Access-Control-Allow-Headers中,不然會報錯。
項目踩坑:
在接口響應中添加了以上允許跨域響應的頭部字段,但是在開發中還報了跨域的錯誤(Response to preflight request doesn't pass access control check: Redirect is not allowed for a preflight request),報錯的大致意思是預檢請求禁止重定向。經過排查,發現是服務端nginx做了HTTP到HTTPS的重定向設置,而我恰好是以http+ip地址的形式發起請求的,那麼請求就被重定向到https了,然而,瀏覽器發起的預檢請求是禁止重定向的,因此報錯了。解決方案就是將請求地址改為https+域名的形式,這樣預檢請求就不會重定向了。