.Net Core WebAPI 利用 IActionFilter 实现请求缓存
Posted 言00FFCC
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net Core WebAPI 利用 IActionFilter 实现请求缓存相关的知识,希望对你有一定的参考价值。
.Net Core WebAPI 利用 IActionFilter 实现请求缓存
本文使用Redis缓存方式
- 1 新建类
首先新建一个缓存类CustomActionCacheAttribute
继承Attribute
,因为需要给方法做标记。再引用并实现IActionFilter
接口
public class CustomActionCacheAttribute: Attribute, IActionFilter
{
// 标记的方法执行前执行
public void OnActionExecuting(ActionExecutingContext context)
{
}
// 标记的方法执行完的回调
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
- 2 注入redis缓存
在 .Net core 中,提供了一个操作所有缓存对象的接口IDistributedCache
,需要引用
using Microsoft.Extensions.Caching.Distributed,然后在类中声明变量。
private IDistributedCache _cache;
因为该Filter标注在方法上,运行时解析,所以无法通过注入的方式来注入该对象
所以需要使用扩展来获取IDistributedCache 对象
- 3 扩展获取DI注入对象
新建静态类CustomDIContainer
代码如下:
/// <summary>
/// 自定义依赖获取容器
/// </summary>
public static class CustomDIContainer
{
private static IHttpContextAccessor _httpContextAccessor;
/// <summary>
/// 配置全局HttpContext
/// </summary>
/// <param name="app"></param>
/// <returns></returns>
public static IApplicationBuilder UseCustomHttpContext(this IApplicationBuilder app)
{
_httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
return app;
}
/// <summary>
/// 当前请求Http上下文
/// </summary>
public static HttpContext Current => _httpContextAccessor.HttpContext;
/// <summary>
/// 获取DI注入的组件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static T GetSerivce<T>() => (T)Current?.RequestServices.GetService(typeof(T));
}
使用到了IHttpContextAccessor
,所以需要在Startup
的ConfigureServices
方法中进行http上下文初始化注入.
public void ConfigureServices(IServiceCollection services)
{
......
// 注入NewtonsoftJson组件
services.AddControllers().AddNewtonsoftJson();
// 注入缓存组件
services.AddDistributedRedisCache(r => r.Configuration = Configuration["Redis:ConnectionString"]);
// 注入请求上下文
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
......
}
- 4 缓存扩展
在缓存中,不单单仅是缓存一个方法,应该做到方法的拓展缓存,既根据不同的传参进行方法返回值的缓存,所以需要对方法的参数进行序列化。
方法如下:
/// <summary>
/// 序列化请求参数
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public string GetParams(HttpContext context)
{
try
{
NameValueCollection form = HttpUtility.ParseQueryString(context.Request.QueryString.ToString());
HttpRequest request = context.Request;
string data = string.Empty;
switch (request.Method)
{
case "POST":
request.Body.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(request.Body, Encoding.UTF8))
{
data = reader.ReadToEndAsync().Result;
data = data.StringReplaceEmpty("{", "}", "\\"", "\\'").Replace(":", "=").Replace(",", "&");
}
break;
case "GET":
//第一步:取出所有get参数
IDictionary<string, string> parameters = new Dictionary<string, string>();
for (int f = 0; f < form.Count; f++)
{
string key = form.Keys[f];
parameters.Add(key, form[key]);
}
// 第二步:把字典按Key的字母顺序排序
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
// 第三步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder();
while (dem.MoveNext())
{
string key = dem.Current.Key;
if (!string.IsNullOrEmpty(key))
query.Append(key).Append("=").Append(dem.Current.Value).Append("&");
}
data = query.ToString().TrimEnd('&');
break;
default:
data = string.Empty;
break;
}
return data;
}
catch
{
return string.Empty;
}
}
并在方法内部声明存储请求参数及方法的字符串
private string _urlParams;
- 5 拓展缓存过期时间
每个方法返回结果都会根据不同的更新频率设置不同的缓存时间。声明属性可供头部标注。
/// <summary>
/// 缓存过期时间[分钟] 默认缓存时间30分钟
/// </summary>
public int ValidTimeMinutes { get; set; } = 30;
- 6 完整代码
public class CustomActionCacheAttribute : Attribute, IActionFilter
{
/// <summary>
/// 缓存过期时间[分钟] 默认缓存时间30分钟
/// </summary>
public int ValidTimeMinutes { get; set; } = 30;
private string _urlParams;
private IDistributedCache _cache;
public void OnActionExecuting(ActionExecutingContext context)
{
// 根据上方拓展的DI容器获取 IDistributedCache 对象
this._cache = CustomDIContainer.GetSerivce<IDistributedCache>();
// 获取调用参数拼装缓存的key
_urlParams = $"{context.HttpContext.Request.Path}?{GetParams(context.HttpContext)}";
// 读取缓存
byte[] cacheValue = _cache.Get(_urlParams);
// 判断是否存在缓存
if (cacheValue == null || cacheValue.Length == 0) return;
// 重新序列为结果
// Deserialize 是自定义的byte[]转指定类型的拓展方法
IActionResult result = cacheValue.Deserialize<string>().Json2Object<ObjectResult>();
// 如果序列化成功,指定方法的 context.Result 将自动返回结果,不再执行方法体
if (result != null)
context.Result = result;
}
public void OnActionExecuted(ActionExecutedContext context)
{
var result = context.Result.ConvertJson();
var options = new DistributedCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(ValidTimeMinutes));
// Serialize 是自定义将String字符串转换为byte[]数组的方法
this._cache.SetAsync(_urlParams, result.Serialize(), options);
}
/// <summary>
/// 序列化请求参数
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public string GetParams(HttpContext context)
{
try
{
NameValueCollection form = HttpUtility.ParseQueryString(context.Request.QueryString.ToString());
HttpRequest request = context.Request;
string data = string.Empty;
switch (request.Method)
{
case "POST":
request.Body.Seek(0, SeekOrigin.Begin);
using (var reader = new StreamReader(request.Body, Encoding.UTF8))
{
data = reader.ReadToEndAsync().Result;
data = data.StringReplaceEmpty("{", "}", "\\"", "\\'").Replace(":", "=").Replace(",", "&");
}
break;
case "GET":
//第一步:取出所有get参数
IDictionary<string, string> parameters = new Dictionary<string, string>();
for (int f = 0; f < form.Count; f++)
{
string key = form.Keys[f];
parameters.Add(key, form[key]);
}
// 第二步:把字典按Key的字母顺序排序
IDictionary<string, string> sortedParams = new SortedDictionary<string, string>(parameters);
IEnumerator<KeyValuePair<string, string>> dem = sortedParams.GetEnumerator();
// 第三步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder();
while (dem.MoveNext())
{
string key = dem.Current.Key;
if (!string.IsNullOrEmpty(key))
query.Append(key).Append("=").Append(dem.Current.Value).Append("&");
}
data = query.ToString().TrimEnd('&');
break;
default:
data = string.Empty;
break;
}
return data;
}
catch
{
return string.Empty;
}
}
}
- 7 最后注意
在.Net Core 3需要配置读取body,在代码中方法执行前和方法执行后中多次使用了HttpContext
的Body参数,所以需要在Startup
中Configure
方法添加可多次读取。
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
......
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});
......
}
- 8 使用
/// <summary>
/// 获取系统的组织架构
/// </summary>
/// <returns></returns>
[HttpGet("get_organization")]
[CustomActionCache(ValidTimeMinutes = 60 * 24)]
public async Task<ExecuteResult<string>> GetOrganizationAsync()
{
// 具体的业务逻辑
......
}
如果文中有错误之处,欢迎各位看官指正,谢谢。如需转载,请指明原出处。
以上是关于.Net Core WebAPI 利用 IActionFilter 实现请求缓存的主要内容,如果未能解决你的问题,请参考以下文章
.Net Core WebAPI 利用 IActionFilter 实现请求缓存