一、认识 Filter 操作筛选器
操作筛选器是一个属性,可应用于控制器操作(或整个控制器),该属性修改操作的执行方式; 可以在执行前后做点什么操作。
服务端缓存:
这里官网是这样描述的:
- OutputCache – 此操作筛选器会缓存控制器操作的输出,以指定的时间量。
但是实际测试下来并没有用。
应该使用
ResponseCache
这里Duration 为缓存60秒
两秒的是第一次请求; 2毫秒的是第二次请求;(两秒的是我设置了一个睡眠, 不是接口问题)
缓存成功,可以用在一些固定的数据,比如地图资源和商品分类等。
筛选器类型
- 授权筛选器 - 实现
IAuthorizationFilter属性。 - 操作筛选器 - 实现
IActionFilter属性。 - 结果筛选器 - 实现
IResultFilter属性。 - 异常筛选器 - 实现
IExceptionFilter属性。
IActionFilter
Log是自定的特性,继承自Attribute特性。
ActionExecutingContext上下文
IResultFilter
ResultExecutedContext上下文
IAuthorizationFilter
AuthorizationFilterContext授权筛选器上下文文档链接
IExceptionFilter
在引发异常时候触发
![]()
ExceptionContext上下文文档链接
二、Token权限校验
/// <summary>
/// 权限验证过滤器, 在控制器上添加Permission特性即可
/// IAsyncActionFilter 异步方法校验
/// </summary>
public class PermissionAttribute : Attribute, IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
//获取请求头携带的token
string token = context.HttpContext.Request.Headers["Authorization"];
if(token != null)
{
//调用方法解析, 如果为null则解析失败不能通过
TokenModelJwt model = JwtHelper.SerializeJwt(token);
if(model != null)
{
await next();
}
}
//设置自定义响应数据
context.Result = new UnauthorizedObjectResult(HttpResult<object>.unauthorized("请先授权"));
}
}
三、异常日志记录
serilog
Serilog 为文件、控制台和 其他地方提供诊断日志记录。它易于设置,具有干净的 API,并且可以在最近的 .NET 平台之间移植
使用
1.添加这两个Nuget包
2.在Program.cs中配置
#region 初始化日志
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Error()
.WriteTo.File(Path.Combine("Logs", @"Log.txt"), rollingInterval: RollingInterval.Day).CreateLogger();
#endregion
3.写入日志
Log.Error("测试日志写入");
过滤器拦截异常写入日志
设置响应缓存
在program.cs中添加以下代码:
//使用响应缓存
app.UseResponseCaching();
app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next();
});
如果不使用缓存的话, 在获取Request.body时Stream的长度为0;(就因为这个,浪费了我半天时间)
配置过滤器
新建一个LogAttribute特性过滤器, 并且继承Attribute和IExceptionFilter
public class LogAttribute : Attribute, IExceptionFilter
{
public async void OnException(ExceptionContext context)
{
StringBuilder buffer = new StringBuilder("\n");
//1、记录报异常的ip
IPAddress ip = context.HttpContext.Connection.RemoteIpAddress;
//转为ipv4的公网地址
if (ip.IsIPv4MappedToIPv6)
{
ip = ip.MapToIPv4();
}
buffer.Append($"公 网 IP:\t\t{ip}\n");
//2、记录控制器名和接口名
buffer.Append($"控制器名:\t\t{context.RouteData.Values["controller"].ToString()}\n");
buffer.Append($"接 口 名:\t\t{context.RouteData.Values["action"].ToString()}\n");
//3、获取请求的参数
HttpRequest request = context.HttpContext.Request;
buffer.Append($"请求方法:\t\t{request.Method}\n");
buffer.Append($"请求路径:\t\t{request.Path}\n");
buffer.Append($"网络协议:\t\t{request.Scheme}\n");
string data = "";
if (request.Method == "GET")
{
//get请求参数, url格式:?value=123&value2=asda 这种
//可以做字符串的处理让他好看些, 反正我是看得懂
data = context.HttpContext.Request.QueryString.Value;
}
else //放在body里的请求参数 json格式
{
//得到请求正文的数据流
Stream stream = request.Body;
//设置当前流的位置
stream.Position = 0;
using (StreamReader sr = new StreamReader(stream))
{
data = await sr.ReadToEndAsync();
};
buffer.Append($"参数类型:\t\t{request.ContentType}\n");
}
if(data != "")
{
buffer.Append($"参 数:\t\t{data}\n");
}
//4、异常写入日志
buffer.Append("异 常:\t\t");
buffer.Append(context.Exception.ToString());
buffer.Append("\n--------------------End--------------------\n\n"); //与下一条记录的拉开距离
Log.Error(buffer.ToString());
}
}
在program.cs中配置,让他全局启用,不用再控制器上标记特性。
//设置日志筛选器的的全局过滤
builder.Services.AddMvc(option =>
{
option.Filters.Add(typeof(LogAttribute));
});
来手动抛两个异常:
写入的日志。