您好,登錄后才能下訂單哦!
這篇文章主要介紹“HTTP中ETag語法及使用方法是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“HTTP中ETag語法及使用方法是什么”文章能幫助大家解決問題。
ETag(Entity Tag)是萬維網協議 HTTP 的一部分。它是 HTTP 協議提供的若干機制中的一種 Web 緩存驗證機制,并且允許客戶端進行緩存協商。這使得緩存變得更加高效,而且節省帶寬。如果資源的內容沒有發生改變,Web 服務器就不需要發送一個完整的響應。
ETag 是一個不透明的標識符,由 Web 服務器根據 URL 上的資源的特定版本而指定。如果 URL 上的資源內容改變,一個新的不一樣的 ETag 就會被生成。ETag 可以看成是資源的指紋,它們能夠被快速地比較,以確定兩個版本的資源是否相同。
需要注意的是 ETag 的比較只對同一個 URL 有意義 —— 不同 URL 上資源的 ETag 值可能相同也可能不同。
ETag: W/"<etag_value>" ETag: "<etag_value>"
W/(可選)
:'W/'(大小寫敏感) 表示使用弱驗證器。弱驗證器很容易生成,但不利于比較。強驗證器是比較的理想選擇,但很難有效地生成。相同資源的兩個弱 Etag 值可能語義等同,但不是每個字節都相同。
"<etag_value>"
:實體標簽唯一地表示所請求的資源。它們是位于雙引號之間的 ASCII 字符串(如 “2c-1799c10ab70” )。沒有明確指定生成 ETag 值的方法。通常是使用內容的散列、最后修改時間戳的哈希值或簡單地使用版本號。比如,MDN 使用 wiki 內容的十六進制數字的哈希值。
在大多數場景下,當一個 URL 被請求,Web 服務器會返回資源和其相應的 ETag 值,它會被放置在 HTTP 響應頭的 ETag 字段中:
HTTP/1.1 200 OK Content-Length: 44 Cache-Control: max-age=10 Content-Type: application/javascript; charset=utf-8 ETag: W/"2c-1799c10ab70"
然后,客戶端可以決定是否緩存這個資源和它的 ETag。以后,如果客戶端想再次請求相同的 URL,將會發送一個包含已保存的 ETag 和 If-None-Match 字段的請求。
GET /index.js HTTP/1.1 Host: localhost:3000 Connection: keep-alive If-None-Match: W/"2c-1799c10ab70"
客戶端請求之后,服務器可能會比較客戶端的 ETag 和當前版本資源的 ETag。如果 ETag 值匹配,這就意味著資源沒有改變,服務器便會發送回一個極短的響應,包含 HTTP “304 未修改” 的狀態。304 狀態碼告訴客戶端,它的緩存版本是最新的,可以直接使用它。
HTTP/1.1 304 Not Modified Cache-Control: max-age=10 ETag: W/"2c-1799c10ab70" Connection: keep-alive
了解完 ETag 相關知識后,基于 koa
、koa-conditional-get
、koa-etag
和 koa-static
這些庫來介紹一下,在實際項目中如何利用 ETag
響應頭和 If-None-Match
請求頭實現資源的緩存控制。
// server.js const Koa = require("koa"); const path = require("path"); const serve = require("koa-static"); const etag = require("koa-etag"); const conditional = require("koa-conditional-get"); const app = new Koa(); app.use(conditional()); // 使用條件請求中間件 app.use(etag()); // 使用etag中間件 app.use( // 使用靜態資源中間件 serve(path.join(__dirname, "/public"), { maxage: 10 * 1000, // 設置緩存存儲的最大周期,單位為秒 }) ); app.listen(3000, () => { console.log("app starting at port 3000"); });
在以上代碼中,使用了 koa-static 中間件來處理靜態資源,這些資源被保存在 public
目錄下。在該目錄下,創建了 index.html 和 index.js 兩個資源文件,文件中的內容分別如下所示:
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>ETag 使用示例</title> <script src="/index.js"></script> </head> <body> <h4>ETag 使用示例</h4> </body> </html>
console.log("大家好");
在啟動完服務器之后,打開 Chrome 開發者工具并切換到 Network 標簽欄,然后在瀏覽器地址欄輸入 http://localhost:3000/ 地址,接著多次訪問該地址(地址欄多次回車)。下圖是多次訪問的結果:
下面以 index.js 為例,來分析上圖中與之對應的 HTTP 報文。對于 index.html 文件,感興趣的小伙伴可以自行分析一下。接下來先來分析首次請求 index.js 文件的報文:
GET /index.js HTTP/1.1 Host: localhost:3000 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache ...
HTTP/1.1 200 OK Content-Length: 44 Cache-Control: max-age=10 ETag: W/"2c-1799c10ab70" ...
在使用了 koa-static 和 koa-etag 中間件之后,index.js 文件首次請求的響應報文中會包含 Cache-Control
和 ETag
的字段信息。Cache-Control
描述的是一個相對時間,在進行緩存命中的時候,都是利用客戶端時間進行判斷,所以相比較 Expires
,Cache-Control
的緩存管理更有效,安全一些。
GET /index.js HTTP/1.1 Host: localhost:3000 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache ...
Request URL: http://localhost:3000/index.js Request Method: GET Status Code: 200 OK (from memory cache) Remote Address: [::1]:3000 Referrer Policy: strict-origin-when-cross-origin
Cache-Control: max-age=10 Connection: keep-alive Content-Length: 44 ETag: W/"2c-1799c10ab70"
由于設置了 index.js 資源文件的最大緩存時間為 10s,所以在 10s 內瀏覽器會直接從緩存中讀取文件的內容。需要注意的是,此時的狀態碼為:Status Code: 200 OK (from memory cache)
。
GET /index.js HTTP/1.1 Host: localhost:3000 Connection: keep-alive If-None-Match: W/"2c-1799c10ab70" Referer: http://localhost:3000/ ...
因為 10s 之后,緩存已經過期了,而且在 index.js
文件首次請求的響應報文中也返回了 ETag
字段。所以此時瀏覽器會發起 If-None-Match
條件請求。這類請求可以用來驗證緩存的有效性,省去不必要的控制手段。
HTTP/1.1 304 Not Modified Cache-Control: max-age=10 ETag: W/"2c-1799c10ab70" Connection: keep-alive ...
因為文件的內容未發生改變,所以 10s 后的響應報文的狀態碼為 304 Not Modified。此外,響應報文中也返回了 ETag
字段。看到這里,有一些小伙伴可能會有疑惑 —— ETag 到底是如何生成的?接下來揭開 koa-etag
中間件背后的秘密。
在前面的示例中,使用了 koa-etag 中間件來實現資源的緩存控制。其實該中間件的實現并不復雜,具體如下所示:
// https://github.com/koajs/etag/blob/master/index.js const calculate = require('etag'); // 省略部分代碼 module.exports = function etag (options) { return async function etag (ctx, next) { await next() const entity = await getResponseEntity(ctx) setEtag(ctx, entity, options) } }
由以上代碼可知,在 koa-etag
中間件內部會先通過 getResponseEntity
函數來獲取響應實體對象,然后再調用 setETag
函數來生成 ETag。而 setETag
函數的實現很簡單,在 setETag
函數內部,會通過 etag
這個第三方庫來生成 ETag。
// https://github.com/koajs/etag/blob/master/index.js function setEtag (ctx, entity, options) { if (!entity) return ctx.response.etag = calculate(entity, options) }
etag
這個庫對外提供了一個 etag
函數來創建 ETag,該函數的簽名如下:
etag(entity, [options])
entity
:用于生成 ETag 的實體,類型支持 Strings
,Buffers
和 fs.Stats
。除了 fs.Stats
對象之外,默認將生成 strong ETag
。
options
:配置對象,支持通過 options.weak
屬性來配置生成 weak ETag。
了解完 etag
函數的參數之后,來看一下該函數的具體實現:
function etag (entity, options) { if (entity == null) { throw new TypeError('argument entity is required') } // 支持fs.Stats對象 // isstats 函數的判斷規則:當前對象是否包含ctime、mtime、ino和size這些屬性 var isStats = isstats(entity) var weak = options && typeof options.weak === 'boolean' ? options.weak : isStats // 參數校驗 if (!isStats && typeof entity !== 'string' && !Buffer.isBuffer(entity)) { throw new TypeError('argument entity must be string, Buffer, or fs.Stats') } // 生成ETag標簽 var tag = isStats ? stattag(entity) // 處理fs.Stats對象 : entitytag(entity) return weak ? 'W/' + tag : tag }
在 etag
函數內部會根據 entity
的類型,執行不同的生成邏輯。如果 entity
是 fs.Stats
對象,則會調用 stattag
函數來創建 ETag。
function stattag (stat) { // mtime:Modified Time,是在寫入文件時隨文件內容的更改而更改,是指文件內容最后一次被修改的時間。 var mtime = stat.mtime.getTime().toString(16) var size = stat.size.toString(16) return '"' + size + '-' + mtime + '"' }
而如果 entity
參數非 fs.Stats
對象,則會調用 entitytag
函數來生成 ETag。其中 entitytag
函數的具體實現如下:
function entitytag (entity) { if (entity.length === 0) { return '"0-2jmj7l5rSw0yVb/vlWAYkK/YBwk"' } // 計算實體對象的哈希值 var hash = crypto .createHash('sha1') .update(entity, 'utf8') .digest('base64') .substring(0, 27) // 計算實體對象的長度 var len = typeof entity === 'string' ? Buffer.byteLength(entity, 'utf8') : entity.length return '"' + len.toString(16) + '-' + hash + '"' }
對于非 fs.Stats
對象來說,在 entitytag
函數內部會使用 sha1
消息摘要算法來生成 hash
值并以 base64
格式輸出,而實際的生成的 hash
值會取前 27 個字符。此外,由以上代碼可知,最終的 ETag 將由實體的長度和哈希值兩部分組成。
需要注意的是,生成 ETag
的算法并不是固定的, 通常是使用內容的散列、最后修改時間戳的哈希值或簡單地使用版本號。
其實除了 ETag
字段之外,大多數情況下,響應頭中還會包含 Last-Modified
字段。它們之間的區別如下:
精確度上,Etag 要優于 Last-Modified。Last-Modified 的時間單位是秒,如果某個文件在 1 秒內被改變多次,那么它們的 Last-Modified 并沒有體現出來修改,但是 Etag 每次都會改變,從而確保了精度;此外,如果是負載均衡的服務器,各個服務器生成的 Last-Modified 也有可能不一致。
性能上,Etag 要遜于 Last-Modified,畢竟 Last-Modified 只需要記錄時間,而 ETag 需要服務器通過消息摘要算法來計算出一個hash 值。
優先級上,在資源新鮮度校驗時,服務器會優先考慮 Etag。即如果條件請求的請求頭同時攜帶 If-Modified-Since
和 If-None-Match
字段,則會優先判斷資源的 ETag 值是否發生變化。
關于“HTTP中ETag語法及使用方法是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。