document/公司/工作/云游戏/using Microsoft.AspNetCore.cs
2024-12-04 14:23:41 +08:00

125 lines
4.2 KiB
C#

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.Caching.Distributed;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace CloudGaming.Code.MiddlewareExtend
{
/// <summary>
/// Redis 缓存中间件
/// </summary>
public class RedisCacheMiddleware
{
private readonly RequestDelegate _next;
private static readonly ConcurrentDictionary<Endpoint, RedisCacheAttribute> _attributeCache = new();
public RedisCacheMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context,
IServiceProvider _serviceProvider,
AppConfig appConfig
)
{
// 检查当前请求是否需要缓存
var cacheAttribute = GetCacheAttribute(context);
if (cacheAttribute == null)
{
await _next(context);
return;
}
var _cache = appConfig.GetRedisDataBase();
// 生成缓存键(基于请求路径和查询字符串)
var cacheKey = GenerateCacheKey(context.Request, appConfig);
// 尝试从 Redis 缓存中获取数据
var cachedResponse = await _cache.StringGetAsync(cacheKey);
if (!cachedResponse.IsNullOrEmpty)
{
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(cachedResponse);
return;
}
// 捕获原始响应流
var originalResponseStream = context.Response.Body;
try
{
using (var memoryStream = new MemoryStream())
{
context.Response.Body = memoryStream;
// 调用下一个中间件
await _next(context);
// 将响应流重新定位到起始位置
memoryStream.Position = 0;
// 读取响应内容
var responseBody = await new StreamReader(memoryStream).ReadToEndAsync();
await _cache.StringSetAsync(cacheKey, responseBody, cacheAttribute.TimeSpan);
// 将内容写回原始响应流
memoryStream.Position = 0;
await memoryStream.CopyToAsync(originalResponseStream);
}
}
finally
{
context.Response.Body = originalResponseStream;
}
}
private RedisCacheAttribute GetCacheAttribute(HttpContext context)
{
var endpoint = context.Features.Get<IEndpointFeature>()?.Endpoint;
if (endpoint == null) return null;
if (_attributeCache.TryGetValue(endpoint, out var cachedAttribute))
{
return cachedAttribute;
}
var attribute = endpoint.Metadata.GetMetadata<RedisCacheAttribute>();
if (attribute != null)
{
_attributeCache.TryAdd(endpoint, attribute);
}
return attribute;
}
private string GenerateCacheKey(HttpRequest request, AppConfig appConfig)
{
var appRequestInfo = new AppRequestConfig(request);
var cacheKey = $"cache:api:{request.Path.Value.Replace('/', '.').TrimStart('.')}:{appRequestInfo.Key}:{(string.IsNullOrEmpty(request.QueryString.Value) ? "default" : request.QueryString)}";
return cacheKey;
}
}
/// <summary>
/// Redis 缓存特性类
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class RedisCacheAttribute : Attribute
{
public TimeSpan TimeSpan { get; }
public RedisCacheAttribute(int durationInSeconds) => TimeSpan = TimeSpan.FromSeconds(durationInSeconds);
public RedisCacheAttribute(int hours, int minutes, int seconds) => TimeSpan = new TimeSpan(hours, minutes, seconds);
public RedisCacheAttribute(int minutes, int seconds) => TimeSpan = new TimeSpan(0, minutes, seconds);
}
}