[HTTP 系列] 第 2 篇 —— HTTP 協議那些事

這裏是《寫給前端工程師的 HTTP 系列》,記得有位大佬曾經說過:「大廠前端面試對 HTTP 的要求比 CSS 還要高」,因而可知 HTTP 的重要程度不可小視。文章寫做計劃以下,視狀況可能有必定的刪減,本篇是該系列的第 2 篇 —— 《HTTP 協議那些事》。這篇文章會涉及到 HTTP 協議,cookie 和 session,HTTP 首部/方法/狀態碼等。javascript

更多文章能夠關注個人 interview 系列html

寫做計劃

  • 從 TCP/UDP 到 DNS 解析前端

  • HTTP 協議那些事java

  • HTTPS / SPDY / HTTP/2 / Websocketgit

  • JWTgithub

  • 網絡安全web

  • 跨域面試

  • 緩存機制算法

  • 瀏覽器原理express

  • 終章:從輸入 url 到頁面呈現發生了什麼

HTTP 協議

超文本傳輸協議(HyperText Transfer Protocol)是基於 TCP/IP 協議,用於分佈式、協做式和超媒體信息系統的應用層協議。HTTP 是萬維網的數據通訊的基礎,它是 無狀態 的協議,默認端口爲 80。HTTP 在 TCP 的基礎上,規定了 Request-Response 的模式,這個模式決定了通信一定由瀏覽器首先發起。

拋去一些複雜的層面,瀏覽器開發者只須要一個 TCP 庫就能夠搞定瀏覽器的網絡通信部分。咱們能夠用 telnet 來作個實驗。

首先鏈接到 yanceyleo.com 的主機。

telnet yanceyleo.com 80
複製代碼

此時,三次握手完成,TCP 鏈接已經創建。輸入下面內容,並 雙擊回車,就能夠獲得服務端響應的內容。下面的報文中,第一行的開頭 GET 爲請求訪問服務器的類型,稱爲 方法 (method);後面的 / 指明瞭請求訪問的資源對象,也叫作請求 URI (request-URI);最後爲 HTTP 版本號,用來表示客戶端使用的 HTTP 版本。第二行則是請求的主機名。

GET / HTTP/1.1
Host: yanceyleo.com
複製代碼

telnet 下的請求和響應

HTTP 是無鏈接、無狀態協議

HTTP 是無狀態 (stateless) 協議,它不會對請求和響應之間通訊狀態進行保存,也就是說 HTTP 協議不會對發送過的請求或響應作持久化處理。使用 HTTP 協議,每當有新的請求發送時,就會有對應的新響應產生。協議自己並不保留以前一切的請求或響應報文信息。這是爲了更快地處理大量事務,確保協議的可伸縮性。

  • 無鏈接

    每次鏈接只處理一個請求,服務端處理完客戶端一次請求,等到客戶端做出迴應以後便斷開鏈接。

  • 無狀態

    是指服務端對於客戶端每次發送的請求都認爲它是一個新的請求,上一次會話和下一次會話沒有聯繫。

cookie

cookie 原理

何爲 cookie 呢?咱們在上面瞭解到 HTTP 是無狀態的,但隨着 Web 的不斷髮展,這種 無狀態 的特性出現了弊端。當你登陸到一家購物網站,在跳轉到該站的其餘頁面時也應該繼續保持登陸狀態。可是由於 HTTP 是無狀態的,因此必須得在瀏覽器端存儲一些信息來標識當前用戶,所以 cookie 應運而生,它一種瀏覽器管理狀態的文件。

cookie 原理

瀏覽器第一次發出請求,服務器會將 cookie 放入到響應請求中,在瀏覽器第二次發請求的時候,會把 cookie 帶過去,因而服務端就會辨別用戶身份。注意:單個 cookie 保存的數據不能超過 4K,不少瀏覽器都限制一個站點最多保存 20 個 cookie。

cookie 在請求頭中有一個 cookie 字段,在響應頭裏有一個 set-cookie 字段。

cookie 是不可跨域的

cookie 自己就是用來保存一些隱私性的字段,基於安全性的考量,必需要保證它是 不可跨域的。咱們能夠作個實驗:先打開 https://google.com,而後在開發者工具中輸入如下代碼:

document.cookie = 'hello=world;path=/;domain=.baidu.com';

document.cookie = 'world=hello;path=/;domain=.google.com';
複製代碼

打開 Application 選項卡,在側邊欄找到 Cookies,能夠發現只有 domain 爲 .google.com 的被成功添加。

cookie 是不可跨域的

cookie 的屬性

咱們經過一個登陸的小例子來了解服務端設置 cookie。首先經過 express application generator 生成一個 Express 工程。本示例的源碼請訪問 express-cookies

接着在 index.html 文件中輸入如下代碼,咱們建立一個輸入用戶名和密碼的界面,在點擊按鈕的時候,經過 fetch 將輸入的值發送給後端。

<fieldset>
  <legend>Login</legend>
  <input id="userName" type="text" placeholder="請輸入用戶名" />
  <input id="userPwd" type="password" placeholder="請輸入密碼" />
  <button id="loginBtn">登陸</button>
</fieldset>

<p>登陸狀態: <span id="result"></span></p>
<script> const userName = document.getElementById('userName'); const userPwd = document.getElementById('userPwd'); const loginBtn = document.getElementById('loginBtn'); const result = document.getElementById('result'); loginBtn.addEventListener('click', function() { const data = { userName: userName.value, userPwd: userPwd.value }; fetch('/login', { method: 'POST', headers: new Headers({ 'Content-Type': 'application/json' }), body: JSON.stringify(data) }) .then(res => { return res.json(); }) .then(json => { result.innerHTML = json.msg; }); }); </script>
複製代碼

當用戶名和密碼匹配時 (假設用戶名和密碼都是 yancey),返回給客戶端一個 cookie 以及登陸成功的 json;不然返回登陸失敗的 json。下面是模擬服務端登陸的接口。

router.post('/login', (req, res, next) => {
  const body = req.body;
  if (body.userName === 'yancey' && body.userPwd === 'yancey') {
    // 設置 cookie
    res.cookie('yancey', 'success');
    res.json({
      success: true,
      msg: '登陸成功'
    });
  } else {
    res.status(401).json({
      success: false,
      msg: '用戶名或密碼錯誤'
    });
  }
});
複製代碼

經過這個例子能夠看到,在 express 中,setCookie 的方式爲:第一個參數傳遞 name,第二個參數傳遞 value注意瀏覽器會將元字符和語義字符以外的字符進行轉義。打開 Chrome 的開發者工具,就能夠看到該 cookie 被添加到瀏覽器上了。或者你在控制檯輸入 document.cookie,一樣能夠看到 cookie 字符串。

這只是一個設置 cookie 的簡單例子,cookie 有 7 種屬性可供使用,咱們一一來了解。

cookie 的屬性

domain

該屬性給 cookie 設置 域名,默認爲當前網站的域名,下面的例子將 domain 設爲 yanceyleo.com,因爲前端頁面是 127.0.0.1,根據同源策略,該條 cookie 不會生效。

res.cookie('domain', 'domian', { domain: 'yanceyleo.com' });
複製代碼

expires / maxAge

這兩個屬性都是設置 cookie 的 過時時間。不一樣的是,expires 接收一個 Date 格式的時間,而 maxAge 接收一個 毫秒時間戳。由於後者更加直觀和簡便,因此建議使用 maxAge

兩個屬性均可以傳遞一個 負值 或者 0,若是瀏覽器已存在同名 cookie,則會清除此 cookie,不然該條 cookie 不會被建立。

下面這個例子是建立一條 cookie,並將該 cookie 的過時時間設爲一天後。

res.cookie('expires', 'expires', {
  expires: new Date(Date.now() + 24 * 60 * 60 * 1000)
});
複製代碼

設置過時時間

接着給該條 cookie 設置一個 「負數」,那麼這條 cookie 就被清除了。

res.cookie('expires', 'expires', {
  expires: new Date(Date.now() - 8 * 60 * 60 * 1000)
});
複製代碼

maxAge 的用法同理,它直接傳遞一個 過時時間 的毫秒數便可。下面的例子是將該條 cookie 的過時時間設爲 7 天后。

res.cookie('maxAge', 'maxAge', {
  maxAge: 7 * 24 * 60 * 60 * 1000
});
複製代碼

那麼不設置過時時間的 cookie 會怎樣呢?當你關閉該網站的時候,這些沒有被設置過時時間的 cookie 就死翹翹了 (這種狀況的 cookie 就好像是 session)。

httpOnly

當該屬性設爲 true 時,document.cookie 將沒法獲取該條 cookie,但服務端能夠照常得到。該屬性能夠有效的避免跨站腳本攻擊 (XSS)。關於網絡安全方面的話題,後面會專門寫一篇文章去講。

res.cookie('httpOnly', 'httpOnly', {
  // 只能被 web server 訪問到,也就是說在瀏覽器輸入 document.cookie 沒法取到該條 cookie,目的是防止 xss
  httpOnly: true
});
複製代碼

path

該屬性給 指定的路徑 添加此 cookie,默認爲 /。以下代碼就是給 users 這個路由設置 cookie (即使在服務端該路徑不存在也會被添加上)。

res.cookie('path', 'path', {
  path: '/users'
});
複製代碼

path 屬性

secure

只有當鏈接是 HTTPS 協議,該 cookie 纔會被添加。該屬性默認爲 fasle。由於我本地的 express 是 HTTP 協議,所以該條 cookie 不會生效。

res.cookie('secure', 'secure', {
  secure: true
});
複製代碼

signed (防篡改簽名)

該屬性是給瀏覽器發送一個加密的 cookie,該屬性默認爲 false。在 express 中,咱們可使用 cookie-parser 插件來建立一個加密後的 cookie。服務端經過該 cookie 的內容和簽名來檢驗它是否 被篡改

首先給 cookieParser 傳入一個 secret。

app.use(cookieParser('forcabarca'));
複製代碼

而後返回一個 sign 後的 cookie。

res.cookie('signed', 'signed', {
  signed: true
});
複製代碼

sign 屬性

在 express 中,咱們可使用 req.cookies 來得到 未加密 的 cookie 對象,能夠經過 req.signedCookies 來得到 已加密 的 cookie 對象。

console.log(req.cookies); // { httpOnly: 'httpOnly' }
console.log(req.signedCookies); // { signed: 'signed' }
複製代碼

document.cookie 字符串轉對象的函數

關於 cookie 就說這麼多,最後附贈一個 document.cookie 字符串轉對象的函數,若是你有更好的實現方式,請在下面留言。

const formatCookie = cookies => {
  const o = {};
  cookies
    .split(';')
    .forEach(value => (o[value.split('=')[0]] = value.split('=')[1]));
  return o;
};
複製代碼

session

session 是服務端使用的一種記錄客戶端狀態的機制,與 cookie 不一樣的是,session 保存在 服務端。當客戶端初次發送請求時 (好比登陸成功),服務端會將用戶信息以某種形式保存在服務端,當再次訪問時只需從該 session 中找到該客戶的狀態便可。

所以,cookie 機制就是經過檢查客戶身上的 「通行證」 來肯定客戶身份,而 session 則是經過檢查服務器上的 「客戶明細表」 來確認客戶身份。session 至關於程序在服務器上創建的一份客戶檔案,客戶來訪的時候只須要查詢客戶檔案表就能夠了。

由於 HTTP 是無狀態的,因此單純的 session 仍不能判斷是否爲究竟是哪一個用戶。所以服務端仍要向客戶端發送一個 maxAge 爲 -1 的 cookie 來做爲不一樣用戶的惟一標識。

固然你也能夠不使用 cookie,你能夠經過重寫 URL 地址的方式來實現。它的原理是將用戶的 seesion id 寫入到 URL 中,當瀏覽器解析新的 URL 時就能夠定位到是哪位用戶。

萬變不離其宗,兩種方式都是要保證用戶信息以某種形式保存到客戶端。更先進的 localStorage,sessionStorage,IndexedDB 也是一樣的道理,這裏不去細說。

HTTP 報文

用於 HTTP 協議交互的信息被稱爲 HTTP 報文。客戶端的報文叫作請求報文,服務端的報文叫作響應報文。HTTP 報文自己是有多行數據構成的字符串文本。

報文格式

160c90cb78e72587.jpg

上面這張圖清晰地展現了請求報文和響應報文的格式,用文字描述大體以下。

CR (Carriage Return,回車符:16 進制 0x0d)

LF (Line Feed,換行符:16 進制 0x0a)

<!--請求報文-->
<method>空格<request-url>空格<version>
<headers>
空行 (CR + LF)
<entity-body>

<!--響應報文-->
<version>空格<status>空格<reason-phrase>
<headers>
空行 (CR + LF)
<entity-body>
複製代碼

壓縮報文

HTTP 協議中有一種被稱爲 內容編碼 的功能,能夠有效的壓縮報文的體積。內容編碼指明應用在實體內容上的編碼格式,並保持實體信息原樣壓縮。內容編碼後的實體由客戶端接收並負責解碼。常見的內容編碼有如下幾種:

  • identity (不作壓縮)

  • compress (UNIX 系統的標準壓縮)

  • gzip (GNU zip, 最多見)

  • deflate (zlib)

  • brotli (Google 出品,必屬精品。比 gzip 的壓縮率還要高 37%+,個人網站已使用 brotli,看下圖)

壓縮報文

分割發送的分塊傳輸編碼

從 HTTP 請求回來,就產生了流式的數據,後續的 DOM 樹構建、CSS 計算、渲染、合成、繪製,都是儘量地流式處理前一步的產出:即不須要等到上一步驟徹底結束,就開始處理上一步的輸出,這樣咱們在瀏覽網頁時,纔會看到逐步出現的頁面。

本質上來講,在 HTTP 通訊過程當中,請求的編碼實體資源還沒有所有傳輸完成以前,瀏覽器沒法顯示請求頁面。在傳輸大容量數據時,經過把數據分割成多塊,能讓瀏覽器逐步顯示頁面。這種把實體主體分塊的功能稱爲分塊傳輸編碼 (Chunked Transfer Code)。

分塊傳輸編碼會將實體主體分爲多個塊,每一個塊都會使用十六進制來標記大小,而實體主體的最後一塊會使用 0 (CR+LF) 來標記。使用分塊傳輸編碼的實體主體會由接收的客戶端負責解碼,恢復到編碼前的實體主體。

HTTP 報文首部

上面的章節咱們說到了 HTTP 的報文,它由三部分組成,分別是:報文首部空行報文主體

對於請求報文,它的首部由方法、URL、HTTP 版本、HTTP 首部字段等部分構成。

Jietu20190501-224131@2x.jpg

對於響應報文,它的首部分別由 HTTP 版本、狀態碼、HTTP 首部字段等部分構成。

Jietu20190501-224131@2x.jpg

首部字段類型

  • 通用首部字段 (General Header Field) 請求報文和響應報文兩方都會使用的首部。

  • 請求首部字段 (Request Header Field) 從客戶端向服務端發送請求報文時使用的首部。補充了請求的附加內容、客戶端信息、響應內容相關優先級等信息。

  • 響應首部字段 (Response Header Field) 從服務端向客戶端返回響應報文時使用的首部。補充了響應的附加內容,也會要求客戶端附加額外的內容信息。

  • 實體首部字段 (Entity Header Field) 針對請求報文和響應報文的實體部分使用的首部。補充了資源內容更新時間等與實體有關的信息。

End-to-end 首部 和 Hop-by-hop 首部

HTTP 首部字段將定義成緩存代理和非緩存代理的行爲,分紅 端到端首部逐條首部

分到 端到端首部 的首部會轉發給請求/響應對應的最終接收目標,且必須保存在由緩存生成的響應中,而且它必須被轉發。

分到 逐跳首部 的首部只對單次轉發有效,會因經過緩存或代理而再也不轉發。在 HTTP/1.1 以後的版本,若是使用逐跳首部,則須要提供 Connection 首部字段。其中 Connection、Keep-Alive、Proxy-Authenticate、、Proxy-Authorization、Trailer、TE、Transfer-Encoding、Upgrade 這 8 個爲逐跳首部,其他都爲端到端首部。

通用首部字段

Cache-Control

該字段用於控制緩存的工做機制,它接受多個參數,中間用逗號隔開。

指令 參數 類型 說明
no-cache 請求/響應都有該字段 若請求中包含該字段,則表示客戶端不接受緩存;若服務端包含該字段,緩存前必須先確認其有效性
no-store 請求/響應都有該字段 不緩存請求或相應的任何內容。no-cache 響應其實是能夠存儲到本地緩存區中的,而 no-store 纔是本地完全不緩存
max-age 單位爲秒,必需 請求/響應都有該字段 當緩存時間小於該值時,客戶端接受緩存的資源,不然請求源服務器,該指令的優先級高於 Expires
max-state 單位爲秒,可省略參數 只有請求擁有該字段 只要有該字段,客戶端就能夠接受過時的緩存
min-fresh 單位爲秒,必需 只有請求擁有該字段 該指令要求緩存服務器返回至少還未過指定時間的緩存資源
no-transform 請求/響應都有該字段 不管在請求仍是響應中,都不容許緩存改變實體主體的媒體類型
only-if-cached 只有請求擁有該字段 表示客戶端僅在緩存服務器本地緩存目標資源的狀況下才會要求去返回
cache-extension - 請求/響應都有該字段 新指令擴展
public 只有響應擁有該字段 可向任意客戶端提供相應的緩存
private 可省略 只有響應擁有該字段 僅向特定用戶返回響應
must-revalidate 只有響應擁有該字段 可緩存,但必須再向源服務器進行一次驗證
proxy-revalidate 只有響應擁有該字段 要求中間緩存服務器對緩存的響應有效性再進行確認
s-maxage 單位爲秒,必需 只有響應擁有該字段 與 max-age 相比,該指令僅適用於公共服務器

Connection

Connection 用於控制再也不轉發給代理的首部字段,還能夠管理持久鏈接。HTTP/1.1 默認是持久鏈接,當服務端明確表示斷開鏈接時,則將 Connection 設爲 Close

Date

Date 表示建立報文的日期和時間,它的格式以下。

date: Sun, 05 May 2019 02:05:37 GMT
複製代碼

Trailer

該字段會事先說明在報文主體後記錄了哪些首部字段,可應用於 HTTP/1.1 分塊傳輸編碼。

Transfer-Encoding

該字段規定了傳輸報文主體時採用的編碼方式,HTTP/1.1 的傳輸編碼方式僅對分塊傳輸編碼有效。

Upgrade

該字段用於檢測 HTTP 協議或者其餘協議是否可使用更高的版本通訊,該字段要和 Connection 字段一塊兒使用。下面的例子是詢問是否可使用 TLS/1.0 協議。對於附有 Upgrade 字段的請求,服務端可返回 101 的狀態碼。

connection: upgrade
upgrade: TLS/1.0
複製代碼

Via

該字段用於追蹤客戶端與服務器之間請求和響應報文的傳輸路徑。

請求首部字段

Accept

該字段通知服務器,用戶代理可以處理的媒體類型及媒體類型的相對優先級。其中用 q 表示權重。下面的例子表示客戶端能夠接受純文本類型或者 HTML 類型,而且接收純文本類型的意願 (權重)爲 0.3。

Accept: text/plain; q=0.3, text/html
複製代碼

Accept-Charset

該字段通知服務器,用戶代理支持的字符集及字符集的相對優先級。該字段應用於內容協商機制的服務器驅動協商。若是服務器不能提供該字段的任何字符集,會報 406 錯誤,所以儘可能不去使用該字段 (我試驗了幾個網站,都沒有此字段)。下面的例子表示客戶端支持 utf-8 和 iso-8859-1,且優先使用 utf-8。

Accept-Charset: utf-8, iso-8859-1;q=0.5
複製代碼

Accept-Encoding

該字段告知服務端,客戶端可以使用的頭部壓縮算法。上面 壓縮報文 已經介紹了幾種壓縮方式,這裏不在贅述。

Accept-Encoding: gzip, deflate, br
複製代碼

Authorization

該字段用於告知服務器,用戶代理的認證信息。下面是我博客後臺管理系統的一個場景,在請求一個須要認證的接口時,須要在請求頭上附帶認證信息。

Authorization: Bearer JWT_TOKEN
複製代碼

Expect

客戶端使用 Expect 來告知服務器,指望出現的某種特定行爲。當服務器沒法理解客戶端的指望而發生錯誤時,會返回 417 狀態碼。

該字段跟狀態碼 100 息息相關,等待狀態碼 100 響應的客戶端在發生請求時,須要指定 Expext: 100-continue。該狀態碼的用途主要是容許客戶端發送帶請求體的請求前,判斷服務器是否願意接收請求。

Expect: 100-continue
複製代碼

From

該字段用來告知服務器使用用戶代理的用戶的 Email。

Host

當以單臺服務器分配多個域名的虛擬主機時,Host 字段就能夠用來肯定相應的主機。

Host: www.abc.com
複製代碼

If-Match

形如 If-xxx 的請求字段均可稱爲條件請求。服務器在收到該類請求後,只有判斷條件爲真時纔會執行請求。

服務器會比對 If-Match 的字段值和資源的 ETag 值,僅當二者一致時,纔會執行請求,不然返回 412 狀態碼。當 If-Match 的字段值爲 * 時,服務器會忽略 ETag 值,只要資源存在就處理請求。

If-Match: W/"pqxe5g29m4"
複製代碼

If-None-Match

與 If-Match 相反,服務器會比對 If-None-Match 的字段值和資源的 ETag 值,僅當二者 不一致 時,纔會執行請求。在 GET 和 HEAD 方法中使用該字段會獲取最新資源。

If-Modified-Since

若是在 If-Modified-Since 字段指定的日期時間後,資源發生了更新,服務器會接受請求。若是資源沒更新過,則返回 304 狀態碼。

該字段值和響應首部字段的 Last-Modifie 字段作比較,下面的例子中顯然最後修改時間要新於 If-Modified-Since 的時間,所以會響應新的資源。

// 請求首部字段
If-Modified-Since: Fri, 01 May 2019 11:20:04 GMT

// 響應首部字段
Last-Modified: Fri, 03 May 2019 11:20:04 GMT
複製代碼

If-Unmodified-Since

若是在 If-Modified-Since 字段指定的日期時間後,資源 未發生 更新,服務器纔會接受請求。若是資源在此以後發生了更新,則報 412 錯誤。

If-Range

該字段值跟 相應頭中的 ETag 或 Date 進行比較,若一致,就做爲範圍請求處理,並返回狀態碼 206,不然直接返回所有資源。

Range

對於只需獲取部分資源的範圍請求,包含首部字段 Range 便可告知服務器資源的指定範圍。接收到附帶 Range 字段的請求的服務器,會在處理請求以後返回狀態碼爲 206 的響應。當沒法處理該範圍請求時,返回 200 狀態碼及所有資源。

Range: bytes=5001-10000
複製代碼

Proxy-Authorization

該字段用於告知代理服務器,用戶代理的認證信息。

Referer

告知服務器請求的 URI 是從哪兒發起的。好比在個人博客 www.yanceyleo.com 請求了 AliOSS 上的一張圖片,那麼請求 AliOSS 服務器的那個請求頭就會附上:

Referer: https://www.yanceyleo.com
複製代碼

固然該單詞正確的拼寫應該是 referrer,但 referer 卻沿用至今。想起一句歌詞:「在漫天風沙裏,望着你遠去,我竟悲傷的不能本身 (已)。」

TE

該字段會告知服務端,客戶端可以處理響應的傳輸編碼方式及相對優先級。它相似於 Accept-Encoding,但用於傳輸編碼。除了指定傳輸編碼,還能夠指定伴隨 trailer 字段的分塊傳輸編碼方式。

TE: gzip, delate;q=0.5

TE: trailers 複製代碼

User-Agent

這個字段再不認識直接回爐重造吧,這裏不去贅述,直接看例子。

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36
複製代碼

響應首部字段

Accept-Ranges

該字段用於告知客戶端,服務器是否能處理範圍請求,可處理時指定爲 bytes,不然爲 none

Accept-Ranges: bytes
複製代碼

Age

該字段用於告知客戶端,源服務器在多久前建立了響應,字段值的單位爲秒。若建立該響應的服務器是緩存服務器,Age 值指的是緩存後的響應再次發起認證到認證完成的時間值 (CDN)。

Age: 500
複製代碼

ETag

ETag 是將資源以字符串的形式作惟一性標識,服務器會爲每份資源分配對應的 ETag 值。當資源更新時,ETag 值也須要更新。

ETag 有 強 Etag 值弱 Etag 值 之分。前者是指不管實體發生多麼細微的變化都會改變其值。而弱 ETag 只用於提示資源是否相同。只有資源發生了根本變化,產生差別時纔會改變 ETag 值,弱 ETag 字段值前面會有 W 標識。前者就比如使用了 {deep: true} 同樣。

下面的代碼是一張圖片的 ETag,顯然一張圖片改變意味着資源的完全改變,所以使用了強 ETag。

ETag: "F8F155B13C6DA43723EEDE3EDBBB4D28"
複製代碼

下面的代碼是請求一個數據接口的 ETag,大多數狀況不會發生根本性的改變,所以使用弱 ETag。

etag: W/"300af-7JrdwEcHHeXMqn/UCrMO0zsO0SY"
複製代碼

Location

Location 字段能夠將響應接收方引導至某個與請求 URI 位置不一樣的資源,該字段通常會配合 3xx 的狀態碼使用。

Location: https://yanceyleo.com
複製代碼

Proxy-Authenticate

該字段會把由代理服務器所要求的認證信息發送給客戶端。

Retry-After

該字段告知客戶端應該在多久以後再次發送請求。當服務器出錯報 503 時,若是服務端知道何時能夠恢復,那麼就應該經過該字段告知客戶端。該字段的字段值能夠是具體的日期時間,也能夠是建立響應後的秒數。

Retry-After: Sat, 04 May 2019 11:26:52 GMT
複製代碼

Server

該字段也是一個常見字段,用於告知客戶端,Web 服務器的名稱。好比我使用了 cloudflare 的 CDN,所以服務器以下所示。

server: cloudflare
複製代碼

Vary

該字段可用於對緩存進行控制,它的字段值接收一系列其餘首部字段名。

vary: Accept-Encoding,Cookie
複製代碼

上面這個例子中,源服務器向代理服務器發送了 vary 字段,代理服務器若要進行緩存,只能對 Accept-Encoding 和 Cookie 進行緩存。

WWW-Authenticate

該字段告知客戶端適用於訪問請求 URI 所指定資源的認證方案和帶參數提示的質詢。

實體首部字段

Allow

該字段會告知客戶端所支持的全部 HTTP 請求方法,當服務端接收到不支持的 HTTP 方法時,會返回 405 狀態碼,並將全部能支持的 HTTP 方法寫入首部字段。

Allow: GET, PUT
複製代碼

Content-Encoding

告知客戶端服務器使用的內容編碼方式。

content-encoding: br
複製代碼

Content-Language

告知客戶端實體主體使用的天然語言。

content-language: zh-CN
複製代碼

Content-Length

該字段代表了實體主體部分的大小,單位是字節。

Content-Length: 4871261
複製代碼

Content-MD5

該字段用於檢查報文主體在傳輸過程當中是否保持完整性,以及確認傳輸到達。服務端對報文主體執行 MD5 算法,獲取一個 128 位的二進制數,再經過 base64 編碼後將結果寫入 Content-MD5 字段值。由於 HTTP 首部沒法記錄二進制值,所以須要經過 Base64 進行處理。客戶端在接收到響應後再對報文主體執行一次相同的 MD5 算法。將計算值於該字段值比較,便可判斷出報文主體的準確性。

Content-MD5: +PFVsTxtpDcj7t4+27tNKA==
複製代碼

Content-Range

該字段告知客戶端做爲響應返回的實體的哪一個部分符合範圍請求,字段值以字節爲單位。

Content-Type

很是常見的字段,用來講明實體主體內對象的媒體類型。

content-type: application/json; charset=utf-8
複製代碼

Expires

該字段將資源失敗的日期告訴客戶端,在 Expires 指定的時間以前,響應的副本會一直被保存。當超過指定的時間後,緩存服務器在請求發送過來時,轉向源服務器請求資源。當首部字段 Cache-Control 有指定的 max-age 時,會優先處理 max-age。

關於緩存機制下一章會詳細去講。

Last-Modified

該字段指明資源的最終修改時間,通常來說,該值就是 Request-URI 指定資源的被修改的時間。

HTTP 方法

方法名 描述
GET GET 請求會顯示請求指定的資源。通常來講 GET 方法應該只用於數據的讀取,而不該當用於會產生反作用的非冪等的操做中。它指望的應該是並且應該是安全的和冪等的。這裏的安全指的是,請求不會影響到資源的狀態。
HEAD HEAD 方法與 GET 方法同樣,都是向服務器發出指定資源的請求。可是,服務器在響應 HEAD 請求時不會回傳資源的內容部分,即:響應主體。這樣,咱們能夠不傳輸所有內容的狀況下,就能夠獲取服務器的響應頭信息。HEAD 方法常被用於客戶端查看服務器的性能。
PUT PUT 請求會身向指定資源位置上傳其最新內容,PUT 方法是冪等的方法。經過該方法客戶端能夠將指定資源的最新數據傳送給服務器取代指定的資源的內容。
POST POST 請求會 向指定資源提交數據,請求服務器進行處理,如:表單數據提交、文件上傳等,請求數據會被包含在請求體中。POST 方法是非冪等的方法,由於這個請求可能會建立新的資源或/和修改現有資源。
TRACE TRACE 請求服務器回顯其收到的請求信息,該方法主要用於 HTTP 請求的測試或診斷。
OPTIONS OPTIONS 請求與 HEAD 相似,通常也是用於客戶端查看服務器的性能。 這個方法會請求服務器返回該資源所支持的全部 HTTP 請求方法,該方法會用'*'來代替資源名稱,向服務器發送 OPTIONS 請求,能夠測試服務器功能是否正常。JavaScript 的 XMLHttpRequest 對象進行 CORS 跨域資源共享時,就是使用 OPTIONS 方法發送嗅探請求,以判斷是否有對指定資源的訪問權限。
DELETE DELETE 請求用於請求服務器刪除所請求 URI(統一資源標識符,Uniform Resource Identifier)所標識的資源。DELETE 請求後指定資源會被刪除,DELETE 方法也是冪等的。
PATCH PATCH 方法出現的較晚,它在 2010 年的 RFC 5789 標準中被定義。PATCH 請求與 PUT 請求相似,一樣用於資源的更新。兩者有如下兩點不一樣:1.PATCH 通常用於資源的部分更新,而 PUT 通常用於資源的總體更新。2.當資源不存在時,PATCH 會建立一個新的資源,而 PUT 只會對已在資源進行更新。
CONNECT CONNECT 方法是 HTTP/1.1 協議預留的,可以將鏈接改成管道方式的代理服務器。一般用於 SSL 加密服務器的連接與非加密的 HTTP 代理服務器的通訊。

GET,HEAD,PUT 和 DELETE 是冪等方法,而 POST 不是冪等的。

HTTP 狀態碼

HTTP 狀態碼負責表示客戶端 HTTP 請求的返回結果、標記服務器端的處理是否正常、通知出現的錯誤等工做。

1xx 信息類狀態碼

狀態碼 狀態碼英文名稱 描述
100 Continue 服務器收到請求的初始部分,請客戶端繼續。
101 Switching Protocols 服務器根據客戶端請求切換協議

1xx 的狀態碼錶示一個臨時的響應,僅由狀態行和可選頭構成,由空行結尾。對該類狀態碼,不須要頭部。該類狀態碼在 HTTP/1.1 引入,所以服務器禁止向 HTTP1.0 的客戶端響應 1xx 狀態碼。

對於 100 (Continue) 狀態碼,客戶端應該繼續它的請求。這個過渡的響應用於告知客戶端,請求的初始部分已經被服務器收到,而且沒有被服務器拒絕。客戶端應該繼續發送剩餘的請求,若是請求已經完成,就忽略這個響應。服務器必須在請求完成後發送一個最終的響應。

100 狀態碼的用途主要是,容許客戶端發送帶請求體的請求前,判斷服務器是否願意接收請求 (經過請求頭)。在某些狀況下,若是服務器在不看請求體的狀況下就拒絕請求時,客戶端仍然發送請求體是不恰當的或低效的。

2xx 成功狀態碼

狀態碼 狀態碼英文名稱 描述
200 OK 請求成功,響應主體包含了具體的數據。最多見,通常 GET 和 POST 請求會返回此狀態碼。
201 Created 已建立,通常 PUT 請求會返回此狀態碼。
202 Accepted 服務器已接收到請求,但還未處理完成。
203 Non-Authoritative Information 非受權信息。請求成功,但元信息不在原始服務器上,而是資源的一個副本。若中間節點上有一份資源副本,但沒法或沒有對它發出的與資源有關的元信息進行驗證,就會出現這種狀況。
204 No Content 響應報文中無主體部分。通常 DELETE 請求會返回此狀態碼。
205 Reset Content 負責告知瀏覽器清除當前頁面中全部 HTML 元素。
206 Partial Content 成功執行一個部分或 Range 請求。客戶端能夠在首部中指定請求某個範圍內的文件。該狀態響應頭部必須包含 Content-Range、Date、以及 ETag 或 Content-Location。

206 狀態碼通常是在下載大文件時會遇到,它表示請求已成功,而且主體包含所請求的數據區間,該數據區間是在請求的 Range 首部指定。下圖中,個人博客在獲取音頻文件時返回了 206 狀態碼。

206 狀態碼

3xx 重定向狀態碼

狀態碼 狀態碼英文名稱 描述
300 Multiple Choices 客戶端請求實際指向多個資源的 URL。客戶端能夠在響應中找到資源列表。
301 Moved Permanently 請求的 URL 已被移除。響應的 Location 首部包含如今所處的位置。
302 Found 與 301 相似,客戶端本次應使用響應中的臨時 URL,未來的請求任使用之前的 URL。
303 See Other 告知客戶端使用另外一個 URL 來獲取資源。其主要目的是,容許 POST 請求的響應將客戶端定向的某一個資源上去。
304 Not Modified 若客戶端發起一個有條件的 GET 請求,而資源未被修改,可使用該狀態碼說明資源未被修改。
305 Use Proxy 必須經過代理來訪問這一資源,代理有 Location 首部給出。須要知道的是,客戶端接收到這一狀態時,不該該假定全部請求都通過代理。
307 Temporary Redirect 和 302 相同。

4xx 客戶端錯誤狀態碼

狀態碼 狀態碼英文名稱 描述
400 Bad Request 告知客戶端它發送了一個錯誤的請求。
401 Unauthorized 與適當首部一同返回,告知客戶端在請求以前先進行認證。
403 Forbidden 請求被拒絕。
404 Not Found 服務器沒法找到請求的 URL。
405 Method Not Allowed 客戶端使用不支持的方法請求 URL。應該在首部使用 Allow 告知客戶端正確的方法。
406 Not Acceptable 服務器端沒法提供與 Accept-Charset 以及 Accept-Language 消息頭指定的值相匹配的響應
407 Proxy Authentication Required 代理服務器要求客戶端驗證。
408 Request Timeout 客戶端完成請求時間過長,服務器能夠關閉連接。
409 Conflict 服務器認爲該請求可能引發衝突。響應主體中應包含衝突的主體的描述。
410 Gone 與 404 相似,只是服務器曾經擁有此資源,後來被移除。
411 Length Required 服務器要求請求報文中包含 Content-Length 首部。
412 Precondition Failed 客戶端發起條件請求,其中有條件失敗。
413 Request Entity Too LargeRequest Entity Too Large 客戶端發送的主體部分比服務器可以活但願處理的要大。
414 Request URI Too Long URL 過長。
415 Unsupported Media Type 服務器沒法理解或沒法支持客戶端發送的內容類型。
416 Requested Range Not Satisfiable 請求範圍無效或沒法知足。
417 Expectation Failed 請求首部包含 Expect 指望,但服務器沒法知足。
429 Too Many Requests 短期內發送了太多請求
431 Request Header Fields Too Large 請求頭太大

5xx 服務端錯誤狀態碼

狀態碼 狀態碼英文名稱 描述
500 Internal Server Error 服務器遇到一個妨礙它提供服務的錯誤。
501 Not Implemented 客戶端發起的請求超出服務器能力範圍,如使用了不支持的方法。
502 Bad Gateway 無效網關。一般不是這上游服務器關閉,而是使用了上游服務器不一樣意協議交換數據。
503 Service Unavailable 服務器暫時沒法提供服務。若服務器知道服務什麼時間可使用,能夠在響應頭中加入 Retry-After 首部說明。
504 Gateway Timeout 於 408 相似,只是這裏的響應來自一個網關或代理,它們在等待另外一個服務器響應對其請求響應時超時。
505 HTTP Version Not Support 服務器收到的請求使用了它沒法支持的協議版本。

總結

這一篇文章主要探討了 HTTP 協議以及它的 無鏈接、無狀態 性,從而引出了 cookie 和 session。接着介紹了 HTTP 的頭部、方法、狀態碼。下一篇會着重講解 HTTP 協議的緩存,敬請期待。

歡迎關注個人微信公衆號:進擊的前端

進擊的前端

參考

《圖解 HTTP》 -- 上野 宣

express 中 cookie 的使用和 cookie-parser 的解讀

談談 cookie

把 cookie 聊清楚

[讀] 這一次,讓咱們再深刻一點 - HTTP 報文

HTTP 狀態碼詳解之 100

你所知道的 3xx 狀態碼

相關文章
相關標籤/搜索