abp vnext 自定义验证过滤器(ValidateFilter)
首先准备好过滤类:ValidateFilter 和 异常处理类:ExceptionFilter
public class ValidateFilter : IActionFilter, ITransientDependency { /// <summary> /// /// </summary> /// <param name="context"></param> public void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { var result = new ResponseModel(); foreach (var item in context.ModelState.Values) { if (item.ValidationState == ModelValidationState.Invalid) { result.Code = 500; result.Message = item.Errors.FirstOrDefault().ErrorMessage; break; } } context.Result = new JsonResult(result); } } /// <summary> /// /// </summary> /// <param name="context"></param> public void OnActionExecuted(ActionExecutedContext context) { } }
public class ExceptionFilter : ExceptionFilterAttribute { /// <summary> /// /// </summary> /// <param name="context"></param> public override void OnException(ExceptionContext context) { var response = new ResponseModel(false, context.Exception.Message); context.Result = new ContentResult { // 返回状态码设置为200,表示成功 StatusCode = StatusCodes.Status500InternalServerError, // 设置返回格式 ContentType = "application/json;charset=gb2312", Content = JsonConvert.SerializeObject(response) }; context.ExceptionHandled = true; } /// <summary> /// /// </summary> /// <param name="context"></param> /// <returns></returns> public override async Task OnExceptionAsync(ExceptionContext context) { await Task.Run(() => { var response = new ResponseModel(false, context.Exception.Message); context.Result = new ContentResult { // 返回状态码设置为200,表示成功 StatusCode = StatusCodes.Status500InternalServerError, // 设置返回格式 ContentType = "application/json;charset=utf-8", Content = JsonConvert.SerializeObject(response) }; context.ExceptionHandled = true; }); } }
然后在XxxHttpApiHostModule的ConfigureServices方法里面添加
context.Services.AddMvc(options => { options.Filters.Add<ExceptionFilter>(); options.Filters.Add<ValidateFilter>(); });
结果打断点没走这个自定义的ValidateFilter。
TMD,asp.net core 3.1用这种方式妥妥的,到了你abp vnext为啥不行了?你咋这么能呢?
曾尝试过放弃,但我不信解决不了这个问题。然后各种百度、谷歌,QQ群咨询。终于在热心人的帮助下,渐渐明白了个大概。
看源码是必须的。
abp vnext的参数验证走的是AbpValidationActionFilter这个过滤器
public class AbpValidationActionFilter : IAsyncActionFilter, ITransientDependency { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { //TODO: Configuration to disable validation for controllers..? if (!context.ActionDescriptor.IsControllerAction() || !context.ActionDescriptor.HasObjectResult()) { await next(); return; } context.GetRequiredService<IModelStateValidator>().Validate(context.ModelState); await next(); } }
其中红色代码非常重要。看到IModelStateValidator了没?找到它的实现:ModelStateValidator。代码:
public class ModelStateValidator : IModelStateValidator, ITransientDependency { public virtual void Validate(ModelStateDictionary modelState) { var validationResult = new AbpValidationResult(); AddErrors(validationResult, modelState); if (validationResult.Errors.Any()) { throw new AbpValidationException( "ModelState is not valid! See ValidationErrors for details.", validationResult.Errors ); } } public virtual void AddErrors(IAbpValidationResult validationResult, ModelStateDictionary modelState) { if (modelState.IsValid) { return; } foreach (var state in modelState) { foreach (var error in state.Value.Errors) { validationResult.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key })); } } } }
通过代码我们发现,在Validate方法里面抛出了AbpValidationException异常,最后格式很丑:
{ "error": { "code": null, "message": "Your request is not valid!", "details": "The following errors were detected during validation.\r\n - 姓名不能为空\r\n - 用户名不能为空\r\n", "data": {}, "validationErrors": [ { "message": "姓名不能为空", "members": [ "fullName" ] }, { "message": "用户名不能为空", "members": [ "userName" ] } ] } }
这种错误信息抛给用户无疑非常不友好。因为我在Dto模型上面添加了特性/注解,我就要返回我自己定义的错误信息。你abp vnext不行啊。
说了这么多怎么解决问题呢?说了这么多怎么解决问题呢?说了这么多怎么解决问题呢?
因为 AbpValidationActionFilter 使用 ModelStateValidator 做的处理,那么我将源码中的类:ModelStateValidator,直接拷贝过来放到HttpApi.Host项目里面。
想到就立马验证,跟abp vnext的一模一样:
public class ModelStateValidator : IModelStateValidator, ITransientDependency { public virtual void Validate(ModelStateDictionary modelState) { var validationResult = new AbpValidationResult(); AddErrors(validationResult, modelState); if (validationResult.Errors.Any()) { throw new AbpValidationException( "ModelState is not valid! See ValidationErrors for details.", validationResult.Errors ); } } public virtual void AddErrors(IAbpValidationResult validationResult, ModelStateDictionary modelState) { if (modelState.IsValid) { return; } foreach (var state in modelState) { foreach (var error in state.Value.Errors) { validationResult.Errors.Add(new ValidationResult(error.ErrorMessage, new[] { state.Key })); } } } }
经过断点测试,是走我拷贝来的ModelStateValidator类的。如果我在Validate方法顶部直接return,不做任何逻辑,就会走我自定义的过滤器ValidateFilter了。
到此结束了?还没有,这种方法可以实现,但总感觉没按照abp vnext的套路出牌。
那么我们就按abp vnext的套路来,直接修改ModelStateValidator的Validate方法
public virtual void Validate(ModelStateDictionary modelState) { foreach (var state in modelState) { foreach (var error in state.Value.Errors) { throw new AbpValidationException(error.ErrorMessage); } } }
这样抛出来的异常就是正常的了,比如异常的Message就是:“姓名不能为空”,而不是一大堆的错误信息了。
{ "code": 500, "message": "姓名不能为空", "data": null, "serverTime": "2021-04-28 11:38:58" }
到此结束了?还没有,还有一种更简单的方式,不需要自己处理ModelStateValidator类,而是给ValidateFilter添加顺序
context.Services.AddMvc(options => { options.Filters.Add<ExceptionFilter>(); options.Filters.Add<ValidateFilter>(-1); });
这样,过滤器ValidateFilter就会优先执行。而且不会走自己封装的ExceptionFilter了。跟AbpValidationActionFilter一点关系都没了。
到此结束了?还没有,还有一种常见的方式,先把AbpValidationActionFilter移除,再添加自定义的Filter,具体类似下面的代码:
var filterMetadata = options.Filters.FirstOrDefault(x => x is ServiceFilterAttribute attribute && attribute.ServiceType.Equals(typeof(AbpValidationActionFilter))); // 移除 AbpValidationActionFilter options.Filters.Remove(filterMetadata); options.Filters.Add<ValidateFilter>();
转 https://www.cnblogs.com/subendong/p/14713532.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2022-03-02 后端怎么防止重复提交?(常用的做法)
2021-03-02 ASP.NET Core - 实现自定义WebApi模型验证
2021-03-02 ASP.NET Core[源码分析篇] - Startup
2021-03-02 ASP.NET Core[源码分析篇] - Authentication认证
2021-03-02 MySQL中实现递归查询
2021-03-02 mysql8.0CTE实现递归查询
2021-03-02 mysql判断时间是否在当前时间内_判断当前时间是否在某时间段内