日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > asp.net >内容正文

asp.net

谈谈ASP.NET Core中的ResponseCaching

發(fā)布時(shí)間:2023/12/4 asp.net 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 谈谈ASP.NET Core中的ResponseCaching 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

前面的博客談的大多數(shù)都是針對(duì)數(shù)據(jù)的緩存,今天我們來(lái)?yè)Q換口味。來(lái)談?wù)勗贏SP.NET Core中的ResponseCaching,與ResponseCaching關(guān)聯(lián)密切的也就是常說(shuō)的HTTP緩存。

在閱讀本文內(nèi)容之前,默認(rèn)各位有HTTP緩存相關(guān)的基礎(chǔ),主要是Cache-Control相關(guān)的。

這里也貼兩篇相關(guān)的博客:

  • 透過(guò)瀏覽器看HTTP緩存

  • HTTP協(xié)議 (四) 緩存

回到正題,對(duì)于ASP.NET Core中的ResponseCaching,本文主要講三個(gè)相關(guān)的小內(nèi)容

  • 客戶端(瀏覽器)緩存

  • 服務(wù)端緩存

  • 靜態(tài)文件緩存

  • 客戶端(瀏覽器)緩存

    這里主要是通過(guò)設(shè)置HTTP的響應(yīng)頭來(lái)完成這件事的。方法主要有兩種:

    其一,直接用Response對(duì)象去設(shè)置。

    這種方式也有兩種寫法,示例代碼如下:

    public IActionResult Index()

    {

    ? ? //直接一,簡(jiǎn)單粗暴,不要拼寫錯(cuò)了就好~~

    ? ? Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.CacheControl] = "public, max-age=600";

    ? ??

    ? ? //直接二,略微優(yōu)雅點(diǎn)

    ? ? //Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue()

    ? ? //{

    ? ? //? ? Public = true,

    ? ? //? ? MaxAge = TimeSpan.FromSeconds(600)

    ? ? //};


    ? ? return View();

    }

    這兩者效果是一樣的,大致如下:

    它們都會(huì)給響應(yīng)頭加上?Cache-Control: public, max-age=600,可能有人會(huì)問(wèn),加上這個(gè)有什么用?

    那我們?cè)賮?lái)看張動(dòng)圖,應(yīng)該會(huì)清晰不少。

    這里事先在代碼里面設(shè)置了一個(gè)斷點(diǎn),正常情況下,只要請(qǐng)求這個(gè)action都是會(huì)進(jìn)來(lái)的。

    但是從上圖可以發(fā)現(xiàn),只是第一次才進(jìn)了斷點(diǎn),其他直接打開的都沒(méi)有進(jìn),而是直接返回結(jié)果給我們了,這也就說(shuō)明緩存起作用了。

    同樣的,再來(lái)看看下面的圖,from disk cache也足以說(shuō)明,它并沒(méi)有請(qǐng)求到服務(wù)器,而是直接從本地返回的結(jié)果。

    注:如果是刷新的話,還是會(huì)進(jìn)斷點(diǎn)的。這里需要區(qū)分好刷新,地址欄回車等行為。不同瀏覽器也有些許差異,這里可以用fiddler和postman來(lái)模擬。

    在上面的做法中,我們將設(shè)置頭部信息的代碼和業(yè)務(wù)代碼混在一起了,這顯然不那么合適。

    下面來(lái)看看第二種方法,也是比較推薦的方法。

    其二,用ResponseCacheAttribute去處理緩存相關(guān)的事情。

    對(duì)于和上面的同等配置,只需要下面這樣簡(jiǎn)單設(shè)置一個(gè)屬性就可以了。

    [ResponseCache(Duration = 600)]
    public IActionResult Index(){ ? ?
    ? ? ? ?return View(); }

    效果和上面是一致的!處理起來(lái)是不是簡(jiǎn)單多了。

    既然這兩種方式都能完成一樣的效果,那么ResponseCache這個(gè)Attribute本質(zhì)也是往響應(yīng)頭寫了相應(yīng)的值。

    但是我們知道,純粹的Attribute并不能完成這一操作,其中肯定另有玄機(jī)!

    翻了一下源碼,可以看到它實(shí)現(xiàn)了IFilterFactory這個(gè)關(guān)鍵的接口。

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

    public class ResponseCacheAttribute : Attribute, IFilterFactory, IOrderedFilter

    {

    ? ? public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)

    ? ? {

    ? ? ? ? //..

    ? ? ? ??

    ? ? ? ? return new ResponseCacheFilter(new CacheProfile

    ? ? ? ? {

    ? ? ? ? ? ? Duration = _duration,

    ? ? ? ? ? ? Location = _location,

    ? ? ? ? ? ? NoStore = _noStore,

    ? ? ? ? ? ? VaryByHeader = VaryByHeader,

    ? ? ? ? ? ? VaryByQueryKeys = VaryByQueryKeys,

    ? ? ? ? });

    ? ? }

    }

    也就是說(shuō),真正起作用的是ResponseCacheFilter這個(gè)Filter,核心代碼如下:

    public void OnActionExecuting(ActionExecutingContext context)

    {

    ? ? var headers = context.HttpContext.Response.Headers;


    ? ? // Clear all headers

    ? ? headers.Remove(HeaderNames.Vary);

    ? ? headers.Remove(HeaderNames.CacheControl);

    ? ? headers.Remove(HeaderNames.Pragma);


    ? ? if (!string.IsNullOrEmpty(VaryByHeader))

    ? ? {

    ? ? ? ? headers[HeaderNames.Vary] = VaryByHeader;

    ? ? }


    ? ? if (NoStore)

    ? ? {

    ? ? ? ? headers[HeaderNames.CacheControl] = "no-store";


    ? ? ? ? // Cache-control: no-store, no-cache is valid.

    ? ? ? ? if (Location == ResponseCacheLocation.None)

    ? ? ? ? {

    ? ? ? ? ? ? headers.AppendCommaSeparatedValues(HeaderNames.CacheControl, "no-cache");

    ? ? ? ? ? ? headers[HeaderNames.Pragma] = "no-cache";

    ? ? ? ? }

    ? ? }

    ? ? else

    ? ? {

    ? ? ? ? headers[HeaderNames.CacheControl] = cacheControlValue;

    ? ? }

    }

    它的本質(zhì)自然就是給響應(yīng)頭部寫了一些東西。

    通過(guò)上面的例子已經(jīng)知道了ResponseCacheAttribute運(yùn)作的基本原理,下面再來(lái)看看如何配置出其他不同的效果。

    下面的表格列出了部分常用的設(shè)置和生成的響應(yīng)頭信息。

    ResponseCache的設(shè)置響應(yīng)頭
    [ResponseCache(Duration = 600, Location = ResponseCacheLocation.Client)]Cache-Control: private, max-age=600
    [ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]Cache-Control:no-cache, no-store
    [ResponseCache(Duration = 60, VaryByHeader = "User-Agent")]Cache-Control : public, max-age=60?
    Vary : User-Agent

    注:如果NoStore沒(méi)有設(shè)置成true,則Duration必須要賦值!

    關(guān)于ResponseCacheAttribute,還有一個(gè)不得不提的屬性:CacheProfileName

    它相當(dāng)于指定了一個(gè)“配置文件”,并在這個(gè)“配置文件”中設(shè)置了ResponseCache的一些值。

    這個(gè)時(shí)候,只需要在ResponseCacheAttribute上面指定這個(gè)“配置文件”的名字就可以了,而不用在給Duration等屬性賦值了。

    在添加MVC這個(gè)中間件的時(shí)候就需要把這些“配置文件”準(zhǔn)備好!

    下面的示例代碼添加了兩份“配置文件”,其中一份名為default,默認(rèn)是緩存10分鐘,還有一份名為Hourly,默認(rèn)是緩存一個(gè)小時(shí),還有一些其他可選配置也用注釋的方式列了出來(lái)。

    services.AddMvc(options =>

    {

    ? ? options.CacheProfiles.Add("default", new Microsoft.AspNetCore.Mvc.CacheProfile

    ? ? {

    ? ? ? ? Duration = 600,? // 10 min

    ? ? });


    ? ? options.CacheProfiles.Add("Hourly", new Microsoft.AspNetCore.Mvc.CacheProfile

    ? ? {

    ? ? ? ? Duration = 60 * 60,? // 1 hour

    ? ? ? ? //Location = Microsoft.AspNetCore.Mvc.ResponseCacheLocation.Any,

    ? ? ? ? //NoStore = true,

    ? ? ? ? //VaryByHeader = "User-Agent",

    ? ? ? ? //VaryByQueryKeys = new string[] { "aaa" }

    ? ? });

    });


    現(xiàn)在“配置文件”已經(jīng)有了,下面就是使用這些配置了!只需要在Attribute上面指定CacheProfileName的名字就可以了。

    示例代碼如下:

    [ResponseCache(CacheProfileName = "default")]

    public IActionResult Index()

    {

    ? ? return View();

    }

    ResponseCacheAttribute中還有一個(gè)VaryByQueryKeys的屬性,這個(gè)屬性可以根據(jù)不同的查詢參數(shù)進(jìn)行緩存!

    但是這個(gè)屬性的使用需要結(jié)合下一小節(jié)的內(nèi)容,所以這里就不展開了。

    注:ResponseCacheAttribute即可以加在類上面,也可以加在方法上面,如果類和方法都加了,會(huì)優(yōu)先采用方法上面的配置。

    服務(wù)端緩存

    先簡(jiǎn)單解釋一下這里的服務(wù)端緩存是什么,對(duì)比前面的客戶端緩存,它是將東西存放在客戶端,要用的時(shí)候就直接從客戶端去取!

    同理,服務(wù)端緩存就是將東西存放在服務(wù)端,要用的時(shí)候就從服務(wù)端去取。

    需要注意的是,如果服務(wù)端的緩存命中了,那么它是直接返回結(jié)果的,也是不會(huì)去訪問(wèn)Action里面的內(nèi)容!有點(diǎn)類似代理的感覺(jué)。

    這個(gè)相比客戶端緩存有一個(gè)好處,在一定時(shí)間內(nèi),“刷新”頁(yè)面的時(shí)候會(huì)從這里的緩存返回結(jié)果,而不用再次訪問(wèn)Action去拿結(jié)果。

    要想啟用服務(wù)端緩存,需要在管道中去注冊(cè)這個(gè)服務(wù),核心代碼就是下面的兩句。

    public void ConfigureServices(IServiceCollection services)

    {

    ? ? services.AddResponseCaching();

    }


    public void Configure(IApplicationBuilder app, IHostingEnvironment env)

    {

    ? ? app.UseResponseCaching();

    }

    當(dāng)然,僅有這兩句代碼,并不能完成這里提到的服務(wù)端緩存。還需要前面客戶端緩存的設(shè)置,兩者結(jié)合起來(lái)才能起作用。

    可以看看下面的效果,

    簡(jiǎn)單解釋一下這張圖,

  • 第一次刷新的時(shí)候,會(huì)進(jìn)入中間件,然后進(jìn)入Action,返回結(jié)果,Fiddler記錄到了這一次的請(qǐng)求

  • 第二次打開新標(biāo)簽頁(yè),直接從瀏覽器緩存中返回的結(jié)果,即沒(méi)有進(jìn)入中間件,也沒(méi)有進(jìn)入Action,Fiddler也沒(méi)有記錄到相關(guān)請(qǐng)求

  • 第三次換了一個(gè)瀏覽器,會(huì)進(jìn)入中間件,直接由緩存返回結(jié)果,并沒(méi)有進(jìn)入Action,此時(shí)Fiddler也將該請(qǐng)求記錄了下來(lái),響應(yīng)頭包含了Age

  • 第三次請(qǐng)求響應(yīng)頭部的部分信息如下:

    Age: 16Cache-Control: public,max-age=600

    這個(gè)Age是在變化的!它就等價(jià)于緩存的壽命。

    如果啟用了日志,也會(huì)看到一些比較重要的日記信息。

    在上一小節(jié)中,我們還有提到ResponseCacheAttribute中的VaryByQueryKeys這個(gè)屬性,它需要結(jié)合ResponseCaching中間件一起用的,這點(diǎn)在注釋中也是可以看到的!

    //

    // Summary:

    //? ? ?Gets or sets the query keys to vary by.

    //

    // Remarks:

    //? ? ?Microsoft.AspNetCore.Mvc.ResponseCacheAttribute.VaryByQueryKeys requires the

    //? ? ?response cache middleware.

    public string[] VaryByQueryKeys { get; set; }

    舉個(gè)例子(不一定很合適)來(lái)看看,假設(shè)現(xiàn)在有一個(gè)電影列表頁(yè)面(http://localhost:5001),可以通過(guò)在URL地址上面加查詢參數(shù)來(lái)決定顯示第幾頁(yè)的數(shù)據(jù)。

    如果代碼是這樣寫的,

    [ResponseCache(Duration = 600)]

    public IActionResult List(int page = 0)

    {

    ? ? return Content(page.ToString());

    }

    結(jié)果就會(huì)像下面這樣,三次請(qǐng)求,返回的都是頁(yè)碼為0的結(jié)果!page參數(shù),壓根就沒(méi)起作用!

    GET http://localhost:5001/Home/List HTTP/1.1

    Host: localhost:5001


    HTTP/1.1 200 OK

    Date: Thu, 05 Apr 2018 07:38:51 GMT

    Content-Type: text/plain; charset=utf-8

    Server: Kestrel

    Content-Length: 1

    Cache-Control: public,max-age=600


    0


    GET http://localhost:5001/Home/List?page=2 HTTP/1.1

    Host: localhost:5001


    HTTP/1.1 200 OK

    Date: Thu, 05 Apr 2018 07:38:51 GMT

    Content-Type: text/plain; charset=utf-8

    Server: Kestrel

    Content-Length: 1

    Cache-Control: public,max-age=600

    Age: 5


    0


    GET http://localhost:5001/Home/List?page=5 HTTP/1.1

    Host: localhost:5001


    HTTP/1.1 200 OK

    Date: Thu, 05 Apr 2018 07:38:51 GMT

    Content-Type: text/plain; charset=utf-8

    Server: Kestrel

    Content-Length: 1

    Cache-Control: public,max-age=600

    Age: 8


    0


    正確的做法應(yīng)該是要指定VaryByQueryKeys,如下所示:

    [ResponseCache(Duration = 600, VaryByQueryKeys = new string[] { "page" })]

    public IActionResult List(int page = 0)

    {

    ? ? return Content(page.ToString());

    }


    這個(gè)時(shí)候的結(jié)果就是和預(yù)期的一樣了,不同參數(shù)都有對(duì)應(yīng)的結(jié)果并且這些數(shù)據(jù)都緩存了起來(lái)。

    GET http://localhost:5001/Home/List HTTP/1.1Host: localhost:5001HTTP/1.1 200 OK Date: Thu, 05 Apr 2018 07:45:13 GMT Content-Type: text/plain; charset=utf-8 Server: Kestrel Content-Length: 1Cache-Control: public,max-age=6000GET http://localhost:5001/Home/List?page=2 HTTP/1.1Host: localhost:5001HTTP/1.1 200 OKDate: Thu, 05 Apr 2018 07:45:22 GMTContent-Type: text/plain; charset=utf-8 Server: Kestrel Content-Length: 1Cache-Control: public,max-age=6002GET http://localhost:5001/Home/List?page=5 HTTP/1.1Host: localhost:5001HTTP/1.1 200 OKDate: Thu, 05 Apr 2018 07:45:27 GMTContent-Type: text/plain; charset=utf-8 Server: Kestrel Content-Length: 1Cache-Control: public,max-age=6005

    ResponseCachingMiddleware在這里是用了MemoryCache來(lái)讀寫緩存數(shù)據(jù)的。如果應(yīng)用重啟了,緩存的數(shù)據(jù)就會(huì)失效,要重新來(lái)過(guò)。

    靜態(tài)文件緩存

    對(duì)于一些常年不變或比較少變的js,css等靜態(tài)文件,也可以把它們緩存起來(lái),避免讓它們總是發(fā)起請(qǐng)求到服務(wù)器,而且這些靜態(tài)文件可以緩存更長(zhǎng)的時(shí)間!

    如果已經(jīng)使用了CDN,這一小節(jié)的內(nèi)容就可以暫且忽略掉了。。。

    對(duì)于靜態(tài)文件,.NET Core有一個(gè)單獨(dú)的StaticFiles中間件,如果想要對(duì)它做一些處理,同樣需要在管道中進(jìn)行注冊(cè)。

    UseStaticFiles有幾個(gè)重載方法,這里用的是帶StaticFileOptions參數(shù)的那個(gè)方法。

    因?yàn)镾taticFileOptions里面有一個(gè)OnPrepareResponse可以讓我們修改響應(yīng)頭,以達(dá)到HTTP緩存的效果。

    //

    // Summary:

    //? ? ?Called after the status code and headers have been set, but before the body has

    //? ? ?been written. This can be used to add or change the response headers.

    public Action<StaticFileResponseContext> OnPrepareResponse { get; set; }


    下面來(lái)看個(gè)簡(jiǎn)單的例子:

    app.UseStaticFiles(new StaticFileOptions

    {

    ? ? OnPrepareResponse = context =>

    ? ? {

    ? ? ? ? context.Context.Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue

    ? ? ? ? {?

    ? ? ? ? ? ? Public = true,

    ? ? ? ? ? ? //for 1 year

    ? ? ? ? ? ? MaxAge = System.TimeSpan.FromDays(365)

    ? ? ? ? };

    ? ? }

    });


    此時(shí)的效果如下:

    一些需要注意的地方

    其一,ResponseCaching中間件對(duì)下面的情況是不會(huì)進(jìn)行緩存操作的!

  • 一個(gè)請(qǐng)求的Status Code不是200

  • 一個(gè)請(qǐng)求的Method不是GETHEAD

  • 一個(gè)請(qǐng)求的Header包含Authorization

  • 一個(gè)請(qǐng)求的Header包含Set-Cookie

  • 一個(gè)請(qǐng)求的Header包含僅有值為*的Vary

  • ...

  • 其二,當(dāng)我們使用了Antiforgery的時(shí)候也要特別的注意!!它會(huì)直接把響應(yīng)頭部的Cache-Control和Pragma重置成no-cache。換句話說(shuō),這兩者是水火不容的!

    詳情可見(jiàn)DefaultAntiforgery.cs#L381

    /// <summary>

    /// Sets the 'Cache-Control' header to 'no-cache, no-store' and 'Pragma' header to 'no-cache' overriding any user set value.

    /// </summary>

    /// <param name="httpContext">The <see cref="HttpContext"/>.</param>

    protected virtual void SetDoNotCacheHeaders(HttpContext httpContext)

    {

    ? ? // Since antifogery token generation is not very obvious to the end users (ex: MVC's form tag generates them

    ? ? // by default), log a warning to let users know of the change in behavior to any cache headers they might

    ? ? // have set explicitly.

    ? ? LogCacheHeaderOverrideWarning(httpContext.Response);


    ? ? httpContext.Response.Headers[HeaderNames.CacheControl] = "no-cache, no-store";

    ? ? httpContext.Response.Headers[HeaderNames.Pragma] = "no-cache";

    }

    當(dāng)然,在某個(gè)頁(yè)面用到了Antiforgery的時(shí)候,也該避免在這個(gè)頁(yè)面使用HTTP緩存!

    它會(huì)在form表單中生成一個(gè)隱藏域,并且隱藏域的值是一個(gè)生成的token ,難道還想連這個(gè)一起緩存?

    總結(jié)

    在.NET Core中用ResponseCaching還是比較簡(jiǎn)單的,雖然還有一些值得注意的地方,但是并不影響我們的正常使用。

    當(dāng)然,最重要的還是合理使用!僅在需要的地方使用!

    最后附上文中Demo的地址 :https://github.com/catcherwong/Demos/tree/master/src/ResponseCachingDemo

    原文地址?https://www.cnblogs.com/catcher1994/p/responsecaching.html


    .NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com

    創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)

    總結(jié)

    以上是生活随笔為你收集整理的谈谈ASP.NET Core中的ResponseCaching的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。