《ASP.NET Core 6框架揭秘》实例演示[34]:缓存整个响应内容

我们利用ASP.NET开发的大部分API都是为了对外提供资源,对于不易变化的资源内容,针对某个维度对其实施缓存可以很好地提供应用的性能。《内存缓存与分布式缓存的使用》介绍的两种缓存框架(本地内存缓存和分布式缓存)为我们提供了简单易用的缓存读写编程模式,本篇介绍的则是针对针对HTTP响应内容实施缓存,ResponseCachingMiddleware中间件赋予我们的能力[本文节选《ASP.NET Core 6框架揭秘》第22章]。

目录
[S2201]基于路径的响应缓存(源代码)
[S2202]基于指定的查询字符串缓存响应(源代码)
[S2203]基于指定的请求报头缓存响应(源代码)
[S2204]缓存屏蔽(源代码)

[S2201]基于路径的响应缓存

为了确定响应内容是否被缓存,如下的演示程序针对路径“/{foobar?}”注册的中间件会返回当前的时间。如代码片段所示,我们调用UseResponseCaching扩展方法对ResponseCachingMiddleware中间件进行了注册, AddResponseCaching扩展方法则注册了该中间件依赖的服务。

using Microsoft.Net.Http.Headers;var app = WebApplication.Create();
app.UseResponseCaching();
app.MapGet("/{foobar}", Process);
app.Run();static DateTimeOffset Process(HttpResponse response)
{response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue{Public = true,MaxAge = TimeSpan.FromSeconds(3600)};return DateTimeOffset.Now;
}

终结点处理方法Process在返回当前时间之前添加了一个Cache-Control响应报头,并且将它的值设置为“public, max-age=3600”(public表示缓存的是可以被所有用户共享的公共数据,而max-age则表示过期时限,单位为秒)。要证明整个响应的内容是否被缓存,只需要验证在缓存过期之前具有相同路径的多个请求对应的响应是否具有相同的主体内容。

GET http://localhost:5000/foo HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:13:39 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Content-Length: 35"2021-12-14T10:13:39.8838806+08:00"
GET http://localhost:5000/foo HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:13:39 GMT
Server: Kestrel
Age: 3
Cache-Control: public, max-age=3600
Content-Length: 35"2021-12-14T10:13:39.8838806+08:00"
GET http://localhost:5000/bar HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:13:49 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Content-Length: 35"2021-12-14T10:13:49.0153031+08:00"
GET http://localhost:5000/bar HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:13:49 GMT
Server: Kestrel
Age: 2
Cache-Control: public, max-age=3600
Content-Length: 35"2021-12-14T10:13:49.0153031+08:00"

如下所示的四组请求和响应是在不同时间发送的,其中两个和后两个请求采用的请求路径分别为“/foo”和“/bar”。可以看出采用相同路径的请求会得到相同的时间戳,意味着后续请求返回的内容来源于缓存,并且说明了响应内容默认是基于请求路径进行缓存的。由于请求发送的时间不同,所以返回的缓存副本的“年龄”(对应响应报头Age)也是不同的。

[S2202]基于指定的查询字符串缓存响应

一般来说,对于提供资源的API来说,请求的路径可以作为资源的标识,所以请求路径决定返回的资源,这也是响应基于路径进行缓存的理论依据。但是在很多情况下,请求路径仅仅是返回内容的决定性因素之一,即使路径能够唯一标识返回的资源,但是资源可以采用不同的语言来表达,也可以采用不同的编码方式,所以最终的响应的内容还是不一样的。在编写请求处理程序的时候,我们还经常根据请求携带的查询字符串来生成响应的内容。以我们的演示的返回当前时间戳的实例来说,我们可以利用请求携带的查询字符串“utc”或者请求报头“X-UTC”来决定返回的是本地时间还是UTC时间。

using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;var app = WebApplication.Create();
app.UseResponseCaching();
app.MapGet("/{foobar?}", Process);
app.Run();static DateTimeOffset Process(HttpResponse response,[FromHeader(Name = "X-UTC")] string? utcHeader,[FromQuery(Name ="utc")]string? utcQuery)
{response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue{Public = true,MaxAge = TimeSpan.FromSeconds(3600)};return Parse(utcHeader) ?? Parse(utcQuery) ?? false? DateTimeOffset.UtcNow : DateTimeOffset.Now;static bool? Parse(string? value)=> value == null? null: string.Compare(value, "1", true) == 0 || string.Compare(value, "true", true) == 0;
}

由于响应缓存默认采用的Key是派生于请求的路径,但是对于我们修改过的这个程序来说,默认的这个缓存键的生成策略就有问题了。程序启动后,我们采用路径“/foobar”发送了如下两个请求,其中第一个请求返回了实时生成的本地时间(+08:00表示北京时间采用的时区),对于第二个情况下,我们本来希望指定“utc”查询字符串以返回一个UTC时间,但是我们得到却是缓存的本地时间。

GET http://localhost:5000/foobar HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:54:54 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Content-Length: 35"2021-12-14T10:54:54.6845646+08:00"
GET http://localhost:5000/foobar?utc=true HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:54:54 GMT
Server: Kestrel
Age: 7
Cache-Control: public, max-age=3600
Content-Length: 35"2021-12-14T10:54:54.6845646+08:00"

[S2203]基于指定的请求报头缓存响应

要解决这个问题,必须要让我们希望的缓存维度作为缓存键的组成部分。就我们演示程序来说,就是得让响应缓存的Key不仅仅包括请求的路径,还应该包括查询字符串“utc”和请求报头“X-UTC”的值。为此我们对演示的程序进行了相应的修改。如下面的代码片段所示,我们从当前HttpContext上下文中提取出IResponseCachingFeature特性,并将设置了它的VaryByQueryKeys属性使之包含了参与缓存的查询字符串的名称“utc”。为了让自定义请求报头“X-UTC”的值也参与缓存,我们将“X-UTC”作为Vary响应报头的值。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.ResponseCaching;
using Microsoft.Net.Http.Headers;var app = WebApplication.Create();
app.UseResponseCaching();
app.MapGet("/{foobar?}", Process);
app.Run();static DateTimeOffset Process(HttpContext httpContext,[FromHeader(Name = "X-UTC")] string? utcHeader,[FromQuery(Name ="utc")]string? utcQuery)
{var response = httpContext.Response;response.GetTypedHeaders().CacheControl = new CacheControlHeaderValue{Public = true,MaxAge = TimeSpan.FromSeconds(3600)};var feature = httpContext.Features.Get<IResponseCachingFeature>()!;feature.VaryByQueryKeys = new string[] { "utc" };response.Headers.Vary = "X-UTC";return Parse(utcHeader) ?? Parse(utcQuery) ?? false ? DateTimeOffset.UtcNow : DateTimeOffset.Now;static bool? Parse(string? value)=> value == null? null: string.Compare(value, "1", true) == 0 || string.Compare(value, "true", true) == 0;
}

对于我们修正过演示程序来说,请求查询字符串“utc”的值会作为响应缓存键的一部分,我们在重启应用后发送了如下针对“/foobar”的四个请求。前两个请求和后两个请求采用相同的查询字符串(“?utc=true”和“?utc=false”),所以后一个请求会返回缓存的内容。

GET http://localhost:5000/foobar?utc=true HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:59:23 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T02:59:23.0540999+00:00"
GET http://localhost:5000/foobar?utc=true HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 02:59:23 GMT
Server: Kestrel
Age: 3
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T02:59:23.0540999+00:00"

从上面给出的报文的内容可以看出,响应报文具有一个值为“X-UTC”的Vary报头,它告诉客户端响应的内容会根据这个名为“X-UTC”的请求报头进行缓存。为了验证这一点,我们在重启应用后针对“/foobar”发送了如下四个请求,前两个请求和后两个请求采用相同的X-UTC(“X-UTC: True”和“X-UTC: False”),所以后一个请求会返回缓存的内容。

GET http://localhost:5000/foobar HTTP/1.1
X-UTC: True
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:05:06 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 34"2021-12-14T03:05:06.977078+00:00"
GET http://localhost:5000/foobar HTTP/1.1
X-UTC: True
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:05:06 GMT
Server: Kestrel
Age: 3
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 34"2021-12-14T03:05:06.977078+00:00"
GET http://localhost:5000/foobar HTTP/1.1
X-UTC: False
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:05:17 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T11:05:17.0068036+08:00"
GET http://localhost:5000/foobar HTTP/1.1
X-UTC: False
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:05:17 GMT
Server: Kestrel
Age: 19
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T11:05:17.0068036+08:00"

响应缓存通过复用已经生成的响应内容来提升性能,但不意味任何请求都适合以缓存的内容予以回复,请求携带的一些报头会屏蔽掉响应缓存。或者更加准确的说法是,客户端请求携带的一些报头会“提醒”服务端当前场景需要返回实时内容。比如携带Authorization报头的请求默认情况下将不会使用缓存的内容予以回复,下面的请求/响应体现了这一点。

GET http://localhost:5000/foobar HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:13:10 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T11:13:10.4605924+08:00"
GET http://localhost:5000/foobar HTTP/1.1
Authorization: foobar
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:13:17 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T11:13:18.0918033+08:00"

关于Authorization请求报头与缓存的关系,它与前面介绍的根据指定的请求报头对响应内容进行缓存是不一样的,当ResponseCachingMiddleware中间件在处理请求时,只要请求携带了此报头,缓存策略将不再使用。如果客户端对数据的实时性要求很高,那么它更希望服务总是返回实时生成的内容,这种情况下它利用利用携带的一些请求报头向服务端传达这样的意图,此时一般会使用到报头“Cache-Control:no-cache”或者“Pragma:no-cache”。这两个请求报头对响应缓存的屏蔽作用体现在如下所示的四组请求/响应中。

GET http://localhost:5000/foobar HTTP/1.1
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:15:16 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 34"2021-12-14T11:15:16.423496+08:00"
GET http://localhost:5000/foobar HTTP/1.1
Cache-Control: no-cache
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:15:26 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T11:15:26.7701298+08:00"
GET http://localhost:5000/foobar HTTP/1.1
Pragma: no-cache
Host: localhost:5000HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 14 Dec 2021 03:15:36 GMT
Server: Kestrel
Cache-Control: public, max-age=3600
Vary: X-UTC
Content-Length: 35"2021-12-14T11:15:36.5283536+08:00"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/280214.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常见端口介绍

Win常用端口 TCP端口&#xff08;静态端口&#xff09;TCP 0 ReservedTCP 1TCP Port Service MultiplexerTCP 2DeathTCP 5Remote Job Entry,yoyoTCP 7EchoTCP 11SkunTCP 12BomberTCP 16SkunTCP 17SkunTCP 18消息传输协议&#xff0c;skunTCP 19SkunTCP 20FTP Data,Amanda TCP 2…

如何更改Windows 10锁定屏幕超时

By default, Windows 10’s lock screen times out and switches off your monitor after one minute. If you’d like it to stick around longer than that–say, if you have background picture you like looking at or you enjoy having Cortana handy–there’s a simple…

ios 开发账号 退出协作_如何在iOS 10中的Notes上进行协作

ios 开发账号 退出协作iOS’ Notes app provides a convenient way to remember the great ideas you come up with and all the things you have to do. The app has evolved over the years, and iOS 10 adds even more features–including collaboration. iOS的Notes应用程…

poj 1182

食物链Time Limit: 1000MS Memory Limit: 10000KTotal Submissions: 86494 Accepted: 25887Description 动物王国中有三类动物A,B,C&#xff0c;这三类动物的食物链构成了有趣的环形。A吃B&#xff0c; B吃C&#xff0c;C吃A。 现有N个动物&#xff0c;以1&#xff0d;N编号。每…

条款6:若不想使用编译器自动生成的函数,就该明确拒绝

如果自己定义的类中并不需要copy assignment操作符或者copy构造函数&#xff0c;为了避免编译器自动生成因为编译器自动生成的没什么用&#xff0c;一般是按照顺序进行赋值或者拷贝&#xff0c;对于有对象内含有指针的话可能会出现一些问题可以在private中声明&#xff08;并不…

为什么Android Geeks购买Nexus设备

The Galaxy S III is the highest-selling Android phone, but much of the geeky buzz is around the Nexus 4 – and the Galaxy Nexus before it. Nexus devices are special because they don’t have some of Android’s biggest problems. Galaxy S III是最畅销的Android…

你的知识死角不能否定你的技术能力

有些事情你不知道&#xff0c;但你一定能解决。 有些人通过我账号资料里的微信加我&#xff0c;然后问我一些所谓“怎么办”的问题&#xff0c;不是我不告诉你&#xff0c;而是我确实不知道。我确实有很高的title&#xff0c;也确实有很多的技术积累&#xff0c;但我并没有达到…

算法练习(十二)

The Suspects Description 严重急性呼吸系统综合症( SARS), 一种原因不明的非典型性肺炎,从2003年3月中旬开始被认为是全球威胁。为了减少传播给别人的机会, 最好的策略是隔离可能的患者。 在Not-Spreading-Your-Sickness大学( NSYSU), 有许多学生团体。同一组的学生经常彼此相…

day4----函数-闭包-装饰器

day4----函数-闭包-装饰器 本文档内容&#xff1a; 1 python中三种名称空间和作用域 2 函数的使用 3 闭包 4 装饰器 一 python中三种名称空间和作用域 1.1名称空间&#xff1a; 当程序运行时&#xff0c;代码从上至下依次执行&#xff0c;它会将变量与值得关系存储在一个空间中…

滤波器和均衡器有什么区别_什么是均衡器,它如何工作?

滤波器和均衡器有什么区别It’s in your car, home theater system, phone, and audio player but it doesn’t have an instruction manual. It’s an equalizer, and with a little know-how you can tweak your audio and fall in love with it all over again. 它在您的汽车…

网络视频监控与人脸识别

明天又要去面试了&#xff0c;趁次机会也将以前做的东西总结一下&#xff0c;为以后理解提供方便&#xff0c;也再加深下印象。 网络视频监控与人脸识别主要由三个程序组成&#xff1a;1、视频采集与传输程序&#xff1b;2、接受与显示程序&#xff1b;3、人脸识别程序。下面就…

esxi.主机配置上联端口_为什么现代的电脑机箱仍然具有USB 2.0端口?

esxi.主机配置上联端口With USB 3.0 becoming more prevalent with each passing year now, you may have found yourself wondering why modern computers still have USB 2.0 ports built into them. With that in mind, today’s SuperUser Q&A post has the answers to…

使用命令导入、导出mysql数据

1.导出全部数据库 利用mysqldump的—all-databases参数可以一口气把你数据库root用户下的所有数据库一口气导出到一个sql文件里。然后&#xff0c;重装系统后使用source命令可以再一口气倒回来。 需要确定mysql安装的路径&#xff1a;本机是&#xff1a;C:\Program Files\MySQL…

面试--跨域--cors

cors是什么 cors 跨域资源共享 Cross-origin resource sharing是一种跨域的解决方案 它允许浏览器向跨源服务器&#xff0c;发出XMLHttpRequest请求&#xff0c;从而克服了AJAX只能同源使用的限制。 但是需要浏览器的支持。值得注意的是&#xff1a;整个CORS通信过程&#xff0…

【原理图操作】原理图更新PCB时未改动元器件布局变动问题?

转载PCB布局、布线完工之后&#xff0c;由于设计功能&#xff0c;发现不完善时, 原理图部分功能需要改动&#xff0c;再改原理图&#xff0c;修改完成后&#xff0c;导入PCB过程中&#xff0c;发现PCB中未改动&#xff08;部分&#xff09;的元器件 布局发生了变化&#xff0c;…

关闭edge任务栏预览_如何在Microsoft Edge中关闭选项卡预览

关闭edge任务栏预览Now that it has extension support, Microsoft Edge is becoming a more and more viable browser. One feature people seem to either love or hate is the pop-up preview you get when you hover over a tab. There’s no built-in setting that lets y…

oracle 创建view时,授权给用户

解决方法&#xff1a; 以dba用户登录 sqlplus / as sysdba 赋予scott用户创建VIEW的权限 grant create view to scott 以scott用户登录oracle conn scott/tiger 创建视图成功 CREATE OR REPLACE VIEW myview AS 转载于:https://www.cnblogs.com/523823-wu/p/7635436.html

[BZOJ 1072] 排列perm

Link&#xff1a; BZOJ 1072 传送门 Solution&#xff1a; 一道直接next_permutation纯暴力就能过的题&#xff1f; 难道2007年时大家都不知道next_permutation这个函数吗 还是用复杂度更优的状压DP吧 设$dp[i][j]$为状态为$i$且对$d$余$j$的个数&#xff0c; 注意$dp[(1<&l…

智能手机丢失 数据安全_丢失智能手机时该怎么办

智能手机丢失 数据安全Phones get stolen or lost everyday. With a plethora of data ripe for identity-theft on it, a lost phone can easily make your blood run cold. Take a deep breath, How-To Geek will talk you through this. 手机每天都会被盗或丢失。 随着大量用…

程序员怎样成为一名架构师?

在今天的技术圈&#xff0c;可能随便遇到一个人递给你一张名片&#xff0c;title 就是某某架构师。架构师多如过江之鲫&#xff0c;也正是眼下业内一个有趣的现象。对于架构师&#xff0c;你有什么看法&#xff1f;什么是架构师&#xff1f;随便打开某招聘网站&#xff1a;系统…