本文使用Redis缓存方式
CustomActionCacheAttribute
继承 Attribute
,因为需要给方法做标记。再引用并实现IActionFilter
接口public class CustomActionCacheAttribute: Attribute, IActionFilter
{
// 标记的方法执行前执行
public void OnActionExecuting(ActionExecutingContext context)
{
}
// 标记的方法执行完的回调
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
IDistributedCache
,需要引用 private IDistributedCache _cache;
因为该Filter标注在方法上,运行时解析,所以无法通过注入的方式来注入该对象
所以需要使用扩展来获取IDistributedCache 对象
CustomDIContainer
///
/// 自定义依赖获取容器
///
public static class CustomDIContainer
{
private static IHttpContextAccessor _httpContextAccessor;
///
/// 配置全局HttpContext
///
///
///
public static IApplicationBuilder UseCustomHttpContext(this IApplicationBuilder app)
{
_httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>();
return app;
}
///
/// 当前请求Http上下文
///
public static HttpContext Current => _httpContextAccessor.HttpContext;
///
/// 获取DI注入的组件
///
///
///
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>();
......
}
///
/// 序列化请求参数
///
///
///
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;
///
/// 缓存过期时间[分钟] 默认缓存时间30分钟
///
public int ValidTimeMinutes {
get; set; } = 30;
public class CustomActionCacheAttribute : Attribute, IActionFilter
{
///
/// 缓存过期时间[分钟] 默认缓存时间30分钟
///
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);
}
///
/// 序列化请求参数
///
///
///
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;
}
}
}
HttpContext
的Body参数,所以需要在 Startup
中Configure
方法添加可多次读取。public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
......
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});
......
}
///
/// 获取系统的组织架构
///
///
[HttpGet("get_organization")]
[CustomActionCache(ValidTimeMinutes = 60 * 24)]
public async Task<ExecuteResult<string>> GetOrganizationAsync()
{
// 具体的业务逻辑
......
}
如果文中有错误之处,欢迎各位看官指正,谢谢。如需转载,请指明原出处。