.NET Core 3.1中如何将Redis Cache作为控制器特性使用?
在.NET Core 3.1中实现Redis缓存控制器特性
问题描述
我正在基于.NET Core 3.1开发项目,请问能否在控制器中把Redis缓存作为特性使用?以下是我的API示例代码:
[SampleAttribute] [Route("/user/getrosetteRewardStatu")] [Authorize(Roles = "SelectedPos", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ProducesResponseType(typeof(BaseResponse<GetRosetteInfoResponse>), 200)] [Consumes("application/json")] [Produces("application/json")] [HttpGet] public async Task<GetrosetteRewardStatuResponse> GetrosetteRewardStatu() { return await mediator.Send(new GetrosetteRewardStatuRequest() { CustomerCode = HttpContext.User.Claims.ToCustomerCode() }); }
请问如何将示例中的[SampleAttribute]改造为Redis缓存特性?是否需要编写中间件?
回答
完全可以把Redis缓存做成控制器特性来用,不需要额外编写中间件,借助ASP.NET Core MVC自带的ActionFilterAttribute就能实现这类缓存逻辑。下面是具体实现步骤:
1. 配置Redis缓存服务
首先确保项目已引入Redis相关NuGet包:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis Install-Package StackExchange.Redis
然后在Startup.cs的ConfigureServices方法中注册Redis缓存服务:
public void ConfigureServices(IServiceCollection services) { // 其他服务注册... // 配置Redis缓存 services.AddStackExchangeRedisCache(options => { options.Configuration = "localhost:6379"; // 替换为你的Redis连接字符串 options.InstanceName = "YourApiPrefix:"; // 缓存键前缀,避免不同项目缓存冲突 }); }
2. 编写自定义Redis缓存特性
创建继承自ActionFilterAttribute的自定义特性,在请求执行前后处理缓存逻辑:
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Caching.Distributed; using System.Text.Json; public class RedisCacheAttribute : ActionFilterAttribute { private readonly int _expireMinutes; // 构造函数,允许指定缓存过期时间,默认5分钟 public RedisCacheAttribute(int expireMinutes = 5) { _expireMinutes = expireMinutes; } public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 从DI容器获取分布式缓存服务 var distributedCache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>(); // 生成唯一缓存键:结合请求路由+当前用户的CustomerCode,确保不同用户缓存独立 var customerCode = context.HttpContext.User.Claims.ToCustomerCode(); var routePath = context.HttpContext.Request.Path; var cacheKey = $"ApiCache:{routePath}:{customerCode}"; // 尝试从Redis读取缓存 var cachedData = await distributedCache.GetStringAsync(cacheKey); if (!string.IsNullOrEmpty(cachedData)) { // 缓存存在:反序列化后直接返回,跳过原Action执行 var cachedResponse = JsonSerializer.Deserialize<GetrosetteRewardStatuResponse>(cachedData); context.Result = new OkObjectResult(cachedResponse); return; } // 缓存不存在:执行原Action获取结果 var executedContext = await next(); // 如果Action执行成功,将结果存入Redis if (executedContext.Result is OkObjectResult okResult && okResult.Value is GetrosetteRewardStatuResponse response) { var serializedResponse = JsonSerializer.Serialize(response); await distributedCache.SetStringAsync(cacheKey, serializedResponse, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(_expireMinutes) }); } } }
3. 在控制器中使用该特性
直接替换原有的[SampleAttribute]为自定义的Redis缓存特性即可,还可以指定过期时间:
[RedisCache(ExpireMinutes = 10)] // 替换SampleAttribute,设置缓存10分钟过期 [Route("/user/getrosetteRewardStatu")] [Authorize(Roles = "SelectedPos", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] [ProducesResponseType(typeof(BaseResponse<GetRosetteInfoResponse>), 200)] [Consumes("application/json")] [Produces("application/json")] [HttpGet] public async Task<GetrosetteRewardStatuResponse> GetrosetteRewardStatu() { return await mediator.Send(new GetrosetteRewardStatuRequest() { CustomerCode = HttpContext.User.Claims.ToCustomerCode() }); }
注意事项
- 缓存键唯一性:如果接口有请求参数,需要把参数加入缓存键生成逻辑,避免不同参数请求共用缓存。
- 序列化方式:示例用了
System.Text.Json,若项目用Newtonsoft.Json,可替换为JsonConvert.SerializeObject和JsonConvert.DeserializeObject。 - 缓存失效:业务数据更新时,需手动删除对应缓存键,保证数据一致性。
内容的提问来源于stack exchange,提问作者ferhat tumer
相关产品推荐
相关产品推荐

