溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用ETag和條件標(biāo)頭進(jìn)行緩存

發(fā)布時(shí)間:2021-07-14 15:33:06 來源:億速云 閱讀:160 作者:chen 欄目:編程語言

這篇文章主要介紹“如何使用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-MatchIf-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)了 etagif-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)接著往下讀!

SetEtag 中間件

正如您可能已經(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è)置回原處。

IfNoneMatch 中間件

這是另一個(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)碼。

IfMatch 中間件

處理 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í)用的文章!

向AI問一下細(xì)節(jié)

免責(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)容。

AI