ASP.NET Core ResponseCaching 提供了缓存http响应内容的能力,通过它可以在本地内存中直接缓存http响应内容,这是速度最快的服务端缓存,省却了网络传输与生成响应内容的开销,是 Memcached 或 Redis 等分布式缓存的有效补充。欲更多了解 ResponseCaching ,推荐阅读园子里的一篇博文 谈谈ASP.NET Core中的ResponseCaching

ResponseCaching 提供了 VaryByHeader 与 VaryByQueryKeys 这两种种方式配置缓存 Key ,我们在使用 VaryByHeader 时发现一个问题 —— 使用 VaryByHeader 时如果不做限制,会面临缓存被撑爆的风险。比如上面根据 Accept 请求头进行缓存,由于客户端可以任意修改请求头,如果有爬虫发出大量的请求,并且每个请求的 Accept 请求头不一样,ResponseCaching 会因此为每个请求生成缓存项,直至内存被耗尽。

为了避免这个问题,我们需要对基于请求头生成缓存 key 的规则进行限制,但 ResponseCaching 没有提供对应的定制能力。

我们开始想到的一个解决方法是基于适配器模式自己实现 IResponseCachingKeyProvider 接口,代码如下:

public class CustomResponseCachingKeyProvider : IResponseCachingKeyProvider
{
private static readonly char KeyDelimiter = '\x1e';
private ResponseCachingKeyProvider _responseCachingKeyProvider; public CustomResponseCachingKeyProvider(ResponseCachingKeyProvider responseCachingKeyProvider)
{
_responseCachingKeyProvider = responseCachingKeyProvider;
} public string CreateBaseKey(ResponseCachingContext context)
{
return _responseCachingKeyProvider.CreateBaseKey(context);
} public IEnumerable<string> CreateLookupVaryByKeys(ResponseCachingContext context)
{
return _responseCachingKeyProvider.CreateLookupVaryByKeys(context);
} public string CreateStorageVaryByKey(ResponseCachingContext context)
{
var key = _responseCachingKeyProvider.CreateStorageVaryByKey(context);
var accept = context.HttpContext.Request.GetTypedHeaders().Accept;
if (accept.Any(x => x.MediaType == "application/json"))
{
key += KeyDelimiter + "accept=application/json";
}
else
{
key += KeyDelimiter + "accept=text/plain";
} return key;
}
}

然后在 Startup 的 ConfigureServices 方法中进行注册

services.AddTransient<ResponseCachingKeyProvider>();
services.AddSingleton<IResponseCachingKeyProvider, CustomResponseCachingKeyProvider>();
services.AddResponseCaching();

除了上面这两步之外,还要给 VaryByHeader 随便赋个值,因为 CreateStorageVaryByKey() 方法只有在 VaryByHeader 或 VaryByQueryKeys 有值的情况下才会被调用。

[ResponseCache(Duration = , VaryByHeader = "_")]

实现后觉得这不是一个优雅的解决方法。

后来尝试修改 ResponseCaching 的源代码,但 ResponseCaching 在设计时并没有考虑到这个场景,修改工作量比较大。

再后来转念一想,不用这么麻烦,可以借助于已有的 VaryByHeader 机制,通过 middleware 根据客户端的请求头生成用于 VaryByHeader 的请求头,middleware 的代码如下

app.Use(async (context, next) =>
{
var accept = context.Request.GetTypedHeaders().Accept;
var mediaTypes = new string[] { "application/json", "text/html" };
var mediaType = accept.Select(x => x.MediaType).FirstOrDefault(x => mediaTypes.Contains(x.Value));
context.Request.Headers.Add("Accept-MediaType", mediaType == null ? "text/plain" : mediaType.Value);
await next.Invoke();
});

ResponseCaching 属性的声明如下

[ResponseCache(Duration = , VaryByHeader = "Accept-MediaType", VaryByQueryKeys = new string[] { "id" })]

折腾了 1 天的问题换了个思路 10 分钟搞定。

另外,使用 ResponseCaching 还需要注意一个地方,如果用到了 CORS ,还需要在 VaryByHeader 中添加 "Origin"

[ResponseCache(Duration = , VaryByHeader = "Accept-MediaType,Origin", VaryByQueryKeys = new string[] { "id" })]

ASP.NET Core ResponseCaching:基于 VaryByHeader 定制缓存 Key的更多相关文章

  1. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  2. ASP.NET Core 使用 Redis 实现分布式缓存:Docker、IDistributedCache、StackExchangeRedis

    ASP.NET Core 使用 Redis 实现分布式缓存:Docker.IDistributedCache.StackExchangeRedis 前提:一台 Linux 服务器.已安装 Docker ...

  3. ASP.NET Core WebApi基于JWT实现接口授权验证

    一.ASP.Net Core WebApi JWT课程前言 我们知道,http协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求时,用户还要再 ...

  4. ASP.NET Core WebApi基于Redis实现Token接口安全认证

    一.课程介绍 明人不说暗话,跟着阿笨一起玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NET WebSer ...

  5. asp.net core 自定义基于 HttpContext 的 Serilog Enricher

    asp.net core 自定义基于 HttpContext 的 Serilog Enricher Intro 通过 HttpContext 我们可以拿到很多有用的信息,比如 Path/QuerySt ...

  6. Asp.Net Core 2.1+的视图缓存(响应缓存)

    响应缓存Razor 页与 ASP.NET 核心 2.0 中不支持. 此功能将支持ASP.NET 核心 2.1 版本. 在老的版本的MVC里面,有一种可以缓存视图的特性(OutputCache),可以保 ...

  7. 详解Asp.Net Core 2.1+的视图缓存(响应缓存)

    响应缓存Razor 页与 ASP.NET 核心 2.0 中不支持. 此功能将支持ASP.NET 核心 2.1 版本. 在老的版本的MVC里面,有一种可以缓存视图的特性(OutputCache),可以保 ...

  8. Asp.net Core, 基于 claims 实现权限验证 - 引导篇

    什么是Claims? 这个直接阅读其他大神些的文章吧,解释得更好. 相关文章阅读: http://www.cnblogs.com/JustRun1983/p/4708176.html http://w ...

  9. (17)ASP.NET Core EF基于数据模型创建数据库

    1.简介 使用Entity Framework Core构建执行基本数据访问的ASP.NET Core MVC应用程序.使用迁移(Migrations)基于数据模型创建数据库,你可以在Windows上 ...

随机推荐

  1. REST API 调用 方法

    METHOD      DESCRIPTION GET         Retrieves the specified resource POST        Creates a resource ...

  2. GDAL多光谱与全色图像融合简单使用

    目录 简述 C++代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 ...

  3. 【转】ASP.NET中验证控件的使用

    前言: 前几日,无奈用JS判断控件的有效性,发现的确是一件费力.费神的事情!特别是针对邮件格式.邮政编码等的关于正则表达式的JS验证(其中涉及正则表达式的比较等,较烦~).其实对于这些常用的控件有效性 ...

  4. Docker Mongo数据库主主同步配置方法

    一.背景 不多说,请看第一篇<Docker Mongo数据库主从同步配置方法> 二.具体操作方法 1.创建目录,如创建~/test/mongo_sr1和-/test/mongo_sr2两个 ...

  5. Linux内核剖析(二)Linux内核绪论

    什么是内核 内核是操作系统最基本的部分.它是为众多应用程序提供对计算机硬件的安全访问的一部分软件,这种访问是有限的,并且内核决定一个程序在什么时候对某部分硬件操作多长时间.内核的分类可分为单内核和双内 ...

  6. php中urlencode与rawurlencode的区别

    前段时间说自己遇到了个<URL加号引发错误>的BUG,引起这个bug的原因就是自己在URL中使用了 urlencode 函数,该函数会把空格转换成加号,这样就导致URL解析出错,而空格只有 ...

  7. MYSQL单表可以存储多少条数据???

    MYSQL单表可以存储多少条数据??? 单表存储四千万条数据,说MySQL不行的自己打脸吧. 多说一句话,对于爬虫来说,任何数据库,仅仅是存储数据的地方,最关心的是 能否存储数据和存储多少数据以及存储 ...

  8. Nginx的location匹配规则

    一 Nginx的location语法 location [=|~|~*|^~] /uri/ { … } =         严格匹配.如果请求匹配这个location,那么将停止搜索并立即处理此请求 ...

  9. Web API中如何获取相对地址的绝对地址 Server.MapPath

    var sPath = System.Web.Hosting.HostingEnvironment.MapPath("/FilePath/");

  10. Intellij 高亮显示与选中字符串相同的内容

    如下图所示,我的是 2018,不同版本,Schema 可能要 Save As一下