您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何使用ETag和條件標(biāo)頭進(jìn)行緩存”,在日常操作中,相信很多人在如何使用ETag和條件標(biāo)頭進(jìn)行緩存問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何使用ETag和條件標(biāo)頭進(jìn)行緩存”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!
Laravel API 性能優(yōu)化:使用 ETag 和條件標(biāo)頭進(jìn)行緩存
當(dāng)寫一個(gè)前后端分離的應(yīng)用時(shí),你必須得開始考慮前端客戶端會(huì)給API提交什么樣的請(qǐng)求,從后端再次獲取數(shù)據(jù),即使你只想要驗(yàn)證前端緩存是否能對(duì)添加的數(shù)據(jù)實(shí)時(shí)更新。根據(jù)以上的需求,你可以使用 ETag
頭 和 conditional requests。
在這篇博文中,我會(huì)簡(jiǎn)單的概括一下 ETag
, If-None-Match
和 If-Match
頭是做什么的,然后再看看我是如何將這些應(yīng)用到我們的package中的,這個(gè)軟件包可以快速的將它實(shí)施到您的應(yīng)用程序中。
讓我們從這一切的核心內(nèi)容開始,那就是 ETag
頭。該頭文件是表示其所在的確切狀態(tài)下的響應(yīng)主體的值。在很多情況下 ETag
的值將是內(nèi)容的 hash
值,因?yàn)檫@是最容易生成和保證響應(yīng)數(shù)據(jù)唯一性標(biāo)識(shí)符的方法。
為了保證 ETag
頭可用,我們必須使用條件請(qǐng)求。我們需要設(shè)置的第一個(gè)條件是 If-None-Match
頭,這是一個(gè)用于 GET
的請(qǐng)求頭。在后端接收到這個(gè)頭之后,需要將它和當(dāng)前的內(nèi)容進(jìn)行比較。如果值匹配的話,將只返回 304
狀態(tài)碼,和獲取實(shí)體資源比較起來,響應(yīng)結(jié)果的數(shù)據(jù)本身是很小的。這一切實(shí)施起來是非常簡(jiǎn)單的:如果你的第一個(gè)獲取資源的 GET
請(qǐng)求返回了一個(gè) ETag
數(shù)據(jù),你的瀏覽器會(huì)自動(dòng)的為接下來的資源的請(qǐng)求配置 If-None-Match
頭。
這意味著,如果您的后端簡(jiǎn)單實(shí)現(xiàn)了 etag
和 if-none-match
,則可以減少從您的API傳輸?shù)角岸说臄?shù)據(jù)量。
第二個(gè)請(qǐng)求條件使用的是 if-match
頭。這被用來阻止 mid-air collisions 。 通俗的說,如果我們想要在后端更新數(shù)據(jù),但是我們的前端數(shù)據(jù)已經(jīng)過時(shí),后端的更新應(yīng)該被終止,而且前端也應(yīng)該有提醒。 這和 if-none-match
的工作方式很類似。在獲取到包含 ETag
值的資源之后,你可以提交一個(gè) PATCH
請(qǐng)求并且設(shè)置一個(gè)和你之前接收到的 ETag
值相等的 If-Match
值。然后,后端將檢查服務(wù)端當(dāng)前可用的資源的 etag
值是否與您發(fā)送的資源相匹配。如果匹配,將允許您的更新。如果沒有匹配,將返回412
狀態(tài)碼,讓前端知道條件不匹配。
如果你想要在 laravel
項(xiàng)目中使用這個(gè)條件請(qǐng)求插件包的話,你可以使用以下命令來安裝:
$ composer require werk365/etagconditionals
然后在你的路由中添加 etag
中間件之后就可以使用了。如果你想研究中間件的工作原理,或者想要不通過我們的插件包來實(shí)現(xiàn)這個(gè)功能的話,請(qǐng)接著往下讀!
正如您可能已經(jīng)猜到的那樣,我們可以通過中間件來很簡(jiǎn)單的實(shí)現(xiàn)這一功能。 Laravel
實(shí)際上已經(jīng)為我們提供了一個(gè) SetCacheHeaders
中間件來設(shè)置 ETag
頭, 但是它不支持 HEAD
請(qǐng)求。 SetEtag
中間件的內(nèi)容看起來是這樣的:
public function handle(Request $request, Closure $next) { // Handle request $method = $request->getMethod(); // Support using HEAD method for checking If-None-Match if ($request->isMethod('HEAD')) { $request->setMethod('GET'); } //Handle response $response = $next($request); // Setting etag $etag = md5($response->getContent()); $response->setEtag($etag); $request->setMethod($method); return $response; }
我們首先要做的事情是獲取請(qǐng)求的方法,以防我們想要修改它。然后,當(dāng)我們?cè)谔幚?HEAD
請(qǐng)求的時(shí)候,我們把它修改為 GET
請(qǐng)求,以確保請(qǐng)求的內(nèi)容已經(jīng)被加載而且可以被加密。在此之后,我們跳轉(zhuǎn)到已經(jīng)使用 md5()
方法加密后的響應(yīng)主體內(nèi)容。在返回響應(yīng)之前,我們會(huì)將加密后的 hash
值作為 ETag
頭,并且將原始請(qǐng)求方法設(shè)置回原處。
這是另一個(gè)相對(duì)簡(jiǎn)單的方法。讓我們先來看看代碼:
public function handle(Request $request, Closure $next) { // Handle request $method = $request->getMethod(); // Support using HEAD method for checking If-None-Match if ($request->isMethod('HEAD')) { $request->setMethod('GET'); } //Handle response $response = $next($request); $etag = '"'.md5($response->getContent()).'"'; $noneMatch = $request->getETags(); if (in_array($etag, $noneMatch)) { $response->setNotModified(); } $request->setMethod($method); return $response; }
這個(gè)開頭與 SetEtag
中間件相似,將確保我們可以再次處理 HEAD
請(qǐng)求,并根據(jù)響應(yīng)內(nèi)容生成 hash值。注意這種情況下我們需要將hash值用雙引號(hào)包裹。雙引號(hào)包裹 ETag
頭,然后在setEtag中間件中 setEtag()
方法自動(dòng)包裹hash。有了hash值后,我們可以輕松的與 If-None-Match
頭進(jìn)行比較。由于該頭可以自動(dòng)加載無限個(gè)hash,并且 getETags()
方法會(huì)將它們以數(shù)組形式返回,所以我們可以核對(duì)新生成的值是否存在于數(shù)組中。如果確實(shí)有匹配,我們可以在響應(yīng)中使用 setNotModified()
設(shè)置 304
的狀態(tài)碼。
處理 If-Match
將稍微復(fù)雜一些。這個(gè)問題歸結(jié)于:我們需要找到一種方法,用來獲取應(yīng)該更新的當(dāng)前版本的內(nèi)容。這可以用多種方式實(shí)現(xiàn)。
你可以使用 HTTP 客戶端對(duì)相同資源從外部發(fā)起 GET
請(qǐng)求
你可以查看當(dāng)前請(qǐng)求將執(zhí)行的操作, 而不是調(diào)用與之等價(jià)的 GET
請(qǐng)求( 例如,調(diào)用控制器上的 show()
方法)
或者你可以通過內(nèi)部發(fā)起一個(gè)新的 GET
請(qǐng)求。
在構(gòu)建這個(gè)中間件時(shí),我開始嘗試使用第二個(gè)選項(xiàng)。出于某種原因,這對(duì)我來說似乎是最好的選擇。我成功地創(chuàng)建了一個(gè)完全可以工作的版本,但我對(duì)結(jié)果并不滿意。為了讓它工作,我需要做一些假設(shè),預(yù)設(shè)一些限制,并做了太多的工作,而我只需要?jiǎng)?chuàng)建一個(gè)新的請(qǐng)求就可以了。
當(dāng)我們要發(fā)起一個(gè)新的請(qǐng)求來獲取當(dāng)前版本資源的時(shí)候,代碼是下面這樣的:
public function handle(Request $request, Closure $next) { // 只有請(qǐng)求方式是 PATCH 并且已經(jīng)設(shè)置了 If-Match 頭 if (! ($request->isMethod('PATCH') && $request->hasHeader('If-Match'))) { return $next($request); } // 對(duì)同一個(gè)點(diǎn)創(chuàng)建新的 GET 請(qǐng)求, // 復(fù)制和添加請(qǐng)求頭,讓中間件能忽略本次請(qǐng)求 $getRequest = Request::create($request->getRequestUri(), 'GET'); $getRequest->headers = $request->headers; $getRequest->headers->set('X-From-Middleware', 'IfMatch'); $getResponse = app()->handle($getRequest); // Get content from response object and get hashes from content and etag $getContent = $getResponse->getContent(); $getEtag = '"'.md5($getContent).'"'; $ifMatch = $request->header('If-Match'); // 比較當(dāng)前和請(qǐng)求攜帶的 hash 值 if ($getEtag !== $ifMatch) { return response(null, 412); } return $next($request);
所有這些中間件都將在請(qǐng)求生命周期開始時(shí)運(yùn)行。首先,我們將過濾掉任何非 PATCH
請(qǐng)求或請(qǐng)求頭中沒有 If Match
的請(qǐng)求。之后,我們將向同一個(gè)端點(diǎn)發(fā)出一個(gè)新的 GET
請(qǐng)求,并從原來的請(qǐng)求中復(fù)制請(qǐng)求頭,以便新請(qǐng)求可以通過身份驗(yàn)證中間件和其他約束。
使用這個(gè)新請(qǐng)求的響應(yīng),我們將再次生成一個(gè)哈希,以便與發(fā)送的哈希進(jìn)行比較。如果哈希匹配,請(qǐng)求將被中間件允許通過。如果不匹配,將返回狀態(tài)代碼為 412
的請(qǐng)求響應(yīng)。
通過使用這三個(gè)中間件,你可以在你的 Laravel 應(yīng)用程序中輕松處理 ETag 和條件請(qǐng)求。
軟件包:https://github.com/365Werk/etagconditionals
原文地址:https://hergen.nl/caching-your-laravel-api-with-etag-and-conditional-requests
譯文地址:https://learnku.com/laravel/t/55539
到此,關(guān)于“如何使用ETag和條件標(biāo)頭進(jìn)行緩存”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。