如何将ASP.NET Core 5 Web API控制器操作的失败模型验证结果包装到另一个类中,并将响应返回为"



我有带有一些操作(方法(的ASP.NET Web API控制器。让我们这样说:

[HttpPost]
public async Task<ActionResult> SavePerson([FromBody]PersonDto person)
{
await _mediatr.Send(new SavePerson.Command(person));
return Ok();
}

PersonDto看起来像这样:

public record PersonDto([Required, MinLength(3)]string Name, int? Age);

当我使用无效人员数据(Name.Length<3等…(调用Web API操作"SavePerson"时,ASP.NET核心模型绑定验证会中断执行并返回400(错误请求(。当我传递有效人员数据时,它运行良好。

我的问题是:

  1. 我如何捕捉这个模型绑定验证结果(400坏请求(并将其转换为不同的格式,这样我们的前端开发人员就会很高兴
  2. 我应该在Web API层中验证我的DTO(PersonDto(,还是最好在Mediator命令处理程序中验证它?我正努力坚持鲍勃叔叔的"干净建筑"。我有域名,应用程序,基础设施,Web API。我的MediatrCQRS处理程序被放置在应用层中

默认情况下会启用自动400错误请求响应。要禁用它,请在StartupConfigureServices方法中使用以下代码:

services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});

然后您可以手动处理无效的模型状态,如下所示:

[HttpPost]
public async Task<ActionResult> SavePerson([FromBody]PersonDto person)
{
if(!ModelState.IsValid)
return BadRequest(ModelState);// or what ever you want
await _mediatr.Send(new SavePerson.Command(person));
return Ok();
}

您可以使用Jason Taylor的Clean Architecture方法。不使用属性验证,而是使用FluentValidation:

public class CreatePersonCommandValidator : AbstractValidator<SavePerson.Command>
{
public CreatePersonCommandValidator()
{
RuleFor(v => v.Title)
.NotEmpty().WithMessage("Title is required.")
.MinimumLength(200).WithMessage("Title at least should have 3 characters.");
}
}

使用MediatR行为执行验证并将错误转换为验证异常:

public class ValidationBehaviour<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehaviour(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
if (_validators.Any())
{
var context = new ValidationContext<TRequest>(request);
var validationResults = await Task.WhenAll(_validators.Select(v => v.ValidateAsync(context, cancellationToken)));
var failures = validationResults.SelectMany(r => r.Errors).Where(f => f != null).ToList();
if (failures.Count != 0)
throw new ValidationException(failures);
}
return await next();
}
}

验证异常:

public class ValidationException : Exception
{
public ValidationException()
: base("One or more validation failures have occurred.")
{
Errors = new Dictionary<string, string[]>();
}
public ValidationException(IEnumerable<ValidationFailure> failures)
: this()
{
Errors = failures
.GroupBy(e => e.PropertyName, e => e.ErrorMessage)
.ToDictionary(failureGroup => failureGroup.Key, failureGroup => failureGroup.ToArray());
}
public IDictionary<string, string[]> Errors { get; }
}

最后,实现一个异常过滤器或异常处理中间件来捕获该异常并返回所需的响应:

public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
private readonly IDictionary<Type, Action<ExceptionContext>> _exceptionHandlers;
public ApiExceptionFilterAttribute()
{
// Register known exception types and handlers.
_exceptionHandlers = new Dictionary<Type, Action<ExceptionContext>>
{
{ typeof(ValidationException), HandleValidationException }
};
}
public override void OnException(ExceptionContext context)
{
HandleException(context);
base.OnException(context);
}
private void HandleException(ExceptionContext context)
{
Type type = context.Exception.GetType();
if (_exceptionHandlers.ContainsKey(type))
{
_exceptionHandlers[type].Invoke(context);
return;
}
if (!context.ModelState.IsValid)
{
HandleInvalidModelStateException(context);
return;
}
HandleUnknownException(context);
}
private void HandleValidationException(ExceptionContext context)
{
var exception = context.Exception as ValidationException;
//var details = new ValidationProblemDetails(exception.Errors)
//{
//Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"
//};

context.Result = Returns your response type //new BadRequestObjectResult(details);
context.ExceptionHandled = true;
}
}

您可以在Api方法的biginig中执行ModelState.isValid((,如果模型无效,则返回BadRequestResult((。您可以将验证错误与BadRequestResult一起返回。

您需要从模型状态中获取验证错误,并填充自定义错误对象。这样,您的客户可以看到更有意义的错误。

相关内容

  • 没有找到相关文章

最新更新