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. 人人网框架导入uidGenerator的ID生成方式

    人人网框架导入uidGenerator的ID生成方式 2019-03-11 LIUREN    SpringBoot2.0  uidGenerator  SpringBoot2.0  uidGener ...

  2. 构建自己的 Smart Life 私有云(二)-> 连通 IFTTT & Slack

    博客搬迁至https://blog.wangjiegulu.com RSS订阅:https://blog.wangjiegulu.com/feed.xml 原文链接:https://blog.wang ...

  3. Effective Java 第三版——81. 优先使用并发实用程序替代wait和notify

    Tips 书中的源代码地址:https://github.com/jbloch/effective-java-3e-source-code 注意,书中的有些代码里方法是基于Java 9 API中的,所 ...

  4. Docker 国内仓库和镜像

    Docker 国内仓库和镜像 由于网络原因,我们在pull Image 的时候,从Docker Hub上下载会很慢...所以,国内的Docker爱好者们就添加了一些国内的镜像(mirror),方便大家 ...

  5. Linux将yum源设置为阿里云的镜像源

    第一步:备份原有镜像源 mv /etc/yum.repo.d/Centos-Base.repo /etc/yum.repo.d/Centos-Base.repo.bak 第二步:下载阿里云的镜像源 w ...

  6. 【转载】vi/vim使用进阶: 指随意动,移动如飞 (一)

    vi/vim使用进阶: 指随意动,移动如飞 (一) << 返回vim使用进阶: 目录 本节所用命令的帮助入口: :help usr_03.txt :help motion.txt :hel ...

  7. 性能测试工具--SIEGE安装及使用简介 siege压力测试

    官方网站http://www.joedog.org/ 概述 Siege是一个多线程http负载测试和基准测试工具.它有3种操作模式: 1) Regression (when invoked by bo ...

  8. vim常用技巧

    # vim常用技巧 ## 行操作------------------------------ 行首 0- 行尾 $- 第一个非空字符 ^ ## 列编辑模式----------------------- ...

  9. 【iCore4 双核心板_uC/OS-II】例程八:消息邮箱

    一.实验说明: 消息邮箱是uC/OS-II中的另一种通信机制,可以使一个任务或者中断服务子程序向另一个任务发送一个指针型的变量.通常该指针指向一个包含了“消息”的特定数据结构.   二.实验截图:   ...

  10. gsoap使用总结

    WebService.soap.gsoap基本概念 WebService服务基本概念:就是一个应用程序,它向外界暴露出一个可以通过web进行调用的API,是分布式的服务组件.本质上就是要以标准的形式实 ...