目录
5.10 TypeFilter和ServiceFilter解读
// 声明builder
var builder = WebApplication.CreateBuilder(args);
// 注册抽象和具体实现之间的关系(两个不同的项目相互调用)
builder.Services.AddTransient<Iservice,Serviceimp>();
// 解决中文乱码问题
builder.Services
.AddControllers(option =>
{
// 全局注册 需要实现IExceptionFilter
option.Filters.Add<CustomExceptionFilterAttribute>();
option.Conventions.Insert(0, new RouteConvention(new RouteAttribute("api/")))
})
.AddJsonOptions(options =>
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);
//注册NLog
builder.Logging.ClearProviders();
builder.Host.UseNLog();
//构建
var app = builder.Build();
app.UseAuthorization();
app.Run();
表示来自IOC容器创建
必然需要IOC容器先注册
如果没有标记【FromService】,默认会认定这个参数是通过调用方传递
标记【FromService】不需要传递参数,由IOC容器提供
[HttpGet() ]
public string FormServiceMethod([FromService] IServiceA _ITServiceA)
{
}
从HTTP Body中去搜索这个参数的数据,通常用于取JSON,XML
Get请求 -- 不能访问
请求API,客服端携带的参数为JSON格式,content-type:application/json
Form表单传参
Get请求 -- 不能访问
Header头信息中收集这个参数的数据,如果客户端通过查询字符串方式传递参数,就是在url地址中去获取值
URL Query收集这个参数的数据
返回指定类型,如果是对象、int,默认返回JSON格式--经过序列化处理的。
如果是字符串,直接返回字符串;
只要是实现了IActionResult接口的,都可以做为返回值
--返回JSON
new JsonResult (object?)
--返回值可以写成
ActionResult<ApiResult<Student>>
固定格式
public class ApiResult<T> where T : class
{
/// <summary>
/// Api执行成功
/// </summary>
public bool Success { get; set }
/// <summary>
/// 错误信息
/// </summary>
public string? Message { get; set }
/// <summary>
/// 结果集
/// </summary>
public T? Data { get; set }
}
需要日志信息持久化,保存在文件中,保存到数据库中
1、Nuget引入程序包log4net + Microsoft.Extensions.Logging.Log4Net.AspNetCore
2、准备配置文件【右击属性,设置为始终复制】
3、植入log4net
builder.Logging.AddLog4Net(“CfgFile/log4net.config”)
4、通过构造函数注入
private readonly ILogger<UserController> _logger;
public UserController(ILogger<UserController> logger)
{
_logger = logger;
}
要支持数据库日志,日志信息写入到数据库中保存;
1、修改配置文件,支持数据库
2、nuget引入:写入数据库需要的程序集
3、执行初始化数据库脚本
1、nuget引入:NLog.web.AspNetCore
2、准备配置文件
3、读取配置文件,植入Nlog.config
//读取配置文件
var logger = NLog.LogManager.Setup().
LoadConfigurationFromAppSettings().GetCurrentClassLogger();
//NLog配置
builder.Logging.ClearProviders(); // 不在系统日志中输出
builder.Host.UseNLog();
要支持数据库日志,日志信息写入到数据库中保存;
1、修改配置文件,支持数据库
2、nuget引入:写入数据库需要的程序集
3、执行初始化数据库脚本
AOP,面向切面编程,可以在不破坏之前的封装为基础动态增加一些功能。
授权--Authorize
资源--Resource
异常--Exception
方法前后--Action
AlwayRunResult
结果前后--Result
1、定义类、实现IResourceFilter/IAsynIResourceFilter接口,继承Attribute父类
2、实现方法
3、标记在API方法上
注意:框架提供的接口,抽象类是不能直接使用的,需要扩展
接口:实现接口,实现方法,把实现类植入;
抽象类:自定义类来继承抽象类,重写方法,把实现类注入;
/Utility/Filters/CustomResourceFilterAttribute
// 自定义ResourceFilter
public class CustomResourceFilterAttribute : Attribute, IResourceFilter
{
// 在API执行之后
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
// 在API执行之前
public void OnResourceExecuting(ResourceExecutingContext context)
{
}
}
// 自定义AsyncResourceFilter
public class CustomAsyncResourceFilterAttribute : Attribute, IAsyncResourceFilter
{
public async Task OnResourceExecutingAsync(ResourceExecutingContext context)
{
// API执行之前
await next.Invoke();
//API执行之后
}
}
// API控制层
// 可以省略Attribute
[CustomResourceFilter]
public void GetUser()
{
}
ResourceFilter天生是为了缓存
缓存:如果缓存区域中的值没有变化,且key不变的,获取的值就应该是之前的值;
// 缓存区域
private static Dictionary<string, object> CacheDictionary = new Dictionary<string, object>();
// 在API执行之后
public void OnResourceExecuted(ResourceExecutedContext context)
{
//保存在缓存中
string key = context.HttpContext.Request.Path
CacheDictionary[key]
}
// 在API执行之前
public void OnResourceExecuting(ResourceExecutingContext context)
{
// 检查缓存,如果有就直接返回(不执行api控制器)
string key = context.HttpContext.Request.Path //Url地址
if (CacheDictionary.ContainsKey(key))
{
object oResult = CacheDictionary[key];
IActionResult result = oResult as IActionResult;
context.Result = result; //请求处理过程中短路器,如果Result赋值了,就不继续执行,如果没有赋值,为null就继续往后执行;
}
}
特点:
和ResourceFilter比较
ResourceFilter使用两个方法包裹了控制器的构造实例+API执行;
ActionFilter使用两个办法只包裹了API逻辑,不包括控制器构造函数
ActionFilter适合什么场景
1、扩展缓存
注意:ResourceFilter更适合做缓存
2、日志记录
API执行结束后,执行结果都是ActionFilter最先获得
1、定义类,实现IexceptionFilter/IAsyncExceptionFilter接口
2、实现方法
3、标记在API方法上
发生异常就会触发扩展类中的实现方法;
/// <summary>
/// 异常处理
/// </summary>
public class CustomExceptionFilterAttribute : Attribute, IExceptionFilter
{
/// <summary>
/// 当有异常发生的时候,触发这个方法
/// </summary>
public void OnException(ExceptionContext context)
{
// 处理异常
if(context.ExceptionHandled==false)//如果没有被处理
{
context.Result = new JsonResult(new ApiResult<object>()
{
Success=false,
Message=context.Exception.Message//异常消息
});
//异常已经处理过了
context.ExceptionHandled = true;
}
}
}
/// <summary>
/// 异步异常处理
/// </summary>
public class CustomAsyncExceptionFilterAttribute : Attribute, IAsyncExceptionFilter
{
/// <summary>
/// 当有异常发生的时候,触发这个方法
/// </summary>
public async Task OnException(ExceptionContext context)
{
// 处理异常
if(context.ExceptionHandled==false)//如果没有被处理
{
context.Result = new JsonResult(new ApiResult<object>()
{
Success=false,
Message=context.Exception.Message//异常消息
});
//异常已经处理过了
context.ExceptionHandled = true;
}
//返回已经完成的Task对象,单线程同步执行的
await Task.CompletedTask;
}
}
API控制台标记
[CustomExceptionFilter] [CustomAsyncExceptionFilter]
前面的Filter都是标记在方法上,如果大批量的生效怎么办呢?
1、当前方法生效
2、控制器下所有方法生效
3、全局生效
大批量生效办法:
1、方法注册--当前方法生效
2、控制器注册--控制器生效
3、全局注册
写在builder.Services.AddControllers中
控制器或者方法上输入
[TypeFilter(typeof(CustomAsyncExceptionFilterAttribute))]
[ServiceFilter(typeof(CustomAsyncExceptionFilterAttribute))] //需要在IOC容器中注入
为了支持ServiceFilter
builder.Services.AddTransient<CustomAsyncExceptionFilterAttribute>();
1、保证数据安全,让不同的人做各自的事,管理更便捷
2、保证系统安全