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. JS获取当前日期、比较日期大小

    //获取当前时间,格式YYYY-MM-DD function getNowFormatDate() { var date = new Date(); var seperator1 = "-& ...

  2. .net core 2.0+superui +Dapper.SimpleCRUD+mysql+NLog

    **_ .net core 2.0+superui +Dapper.SimpleCRUD+mysql+NLog _** 前端框架 superui http://www.supermgr.cn/ 1.组 ...

  3. CPU与GPU性能的比较报告

    运行时间分析 不同的模型在cpu和gpu下的时间差异较大,一般来说gpu会比cpu快5-20倍.我们选用了最常用的inception v3的分类模型,输入图片尺寸为:3x299x299. GPU 在一 ...

  4. vue实例-转载

    http://blog.csdn.net/yuanyuanispeak/article/details/73526795

  5. 【ML入门系列】(一)训练集、测试集和验证集

    训练集.验证集和测试集这三个名词在机器学习领域极其常见,但很多人并不是特别清楚,尤其是后两个经常被人混用. 在有监督(supervise)的机器学习中,数据集常被分成2~3个,即:训练集(train ...

  6. EditText小记

    今天在编写样式的时候,需要设置数据输入为单行,但是 Android:singleLine=”true” 显示为已过期,提示使用 android:maxLines=“1” 代替,但是设置后却发现并没有效 ...

  7. Linux 文件普通权限_011

    一.文件权限10个字符对应类型和权限 二.Linux普通文件和Linux目录读.写.执行权限说明 标注:Linux 中的文件名是存在于父目录的block里面,并指向这个文件的inode节点 1.lin ...

  8. Java线程生命周期

    当你需要使用Java线程在多线程环境下进行编程时,理解Java的线程周期与线程的状态是非常重要的.通过实现Runnale接口或者继承Thread类,我们可以创建线程,为了启动一个线程,我们需要创建一个 ...

  9. Hive学习笔记——安装和内部表CRUD

    1.首先需要安装Hadoop和Hive 安装的时候参考 http://blog.csdn.net/jdplus/article/details/46493553 安装的版本是apache-hive-2 ...

  10. tensorflow中一种融合多个模型的方法

    1.使用场景 假设我们有训练好的模型A,B,C,我们希望使用A,B,C中的部分或者全部变量,合成为一个模型D,用于初始化或其他目的,就需要融合多个模型的方法 2.如何实现 我们可以先声明模型D,再创建 ...