如何使“发生一个或多个验证错误”引发异常?

时间:2020-07-06 14:17:48

标签: asp.net-core asp.net-core-3.1

我正在Core 3.1和我的一个端点上运行WebAPI,但JSON除外,其模型的字段具有{ "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1", "title": "One or more validation errors occurred.", "status": 400, "traceId": "|7ced8b82-4aa34d65daa99a12.", "errors": { "Vendor.UID": [ "UID is required" ] } } 属性,如下所示:

public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
            Common.Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
            services.AddMvc().AddXmlDataContractSerializerFormatters();
            services.AddMvc().AddMvcOptions(options =>
            {
                options.EnableEndpointRouting = false;
            });
            services.AddMvcCore(options => options.OutputFormatters.Add(new XmlSerializerOutputFormatter()));
            services.AddOptions();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
        {

            //Middleware for exception filtering
            app.UseMiddleware<ErrorHandlingMiddleware>(new ErrorHandlingMiddlewareOptions { logger = logger });

            app.UseStaticFiles();            

            app.UseHttpsRedirection();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });

            app.UseMvc(routes =>
            {
                routes.MapRoute("EndpointNotFound", "{*url}", new { controller = "Error", action = "EndpointNotFound" });
            });
        }
    }

当我在未设置UID的情况下调用此端点时,将得到预期的以下输出:

{{1}}

虽然此输出非常有用,但显然与我的API通过ExceptionFilter产生的其他错误输出不一致。还有什么办法可以将这些错误发送给异常过滤器?

这是我的Startup.cs:

{{1}}

5 个答案:

答案 0 :(得分:1)

为了实现此功能,您需要实现此问题中描述的自己的模型验证器:Model validation in Web API - Exception is thrown with out a throw statement?

答案 1 :(得分:1)

您可以在mvc服务或控制器服务中添加过滤器

此过滤器返回badrequest

services.AddControllers(option =>
{
             option.Filters.Add<ValidationFilter>();
});

要创建过滤器,您可以添加此类 您也可以根据需要定制此过滤器

public class ValidationFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //before contrller

            if(!context.ModelState.IsValid)
            {
                var errorsInModelState = context.ModelState
                    .Where(x => x.Value.Errors.Count > 0)
                    .ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Errors.Select(x => x.ErrorMessage).ToArray());

                var errorResponse = new ErrorResponse();

                foreach (var error in errorsInModelState)
                {
                    foreach (var subError in error.Value)
                    {
                        var errorModel = new ErrorModel
                        {
                            FieldName = error.Key,
                            Message = subError
                        };

                        errorResponse.Error.Add(errorModel);
                    }

                    context.Result = new BadRequestObjectResult(errorResponse);
                    return;
                }

                await next();

                //after controller  
            }
        }
    }

我已经像这样创建了错误模型

public class ErrorModel
    {
        public string FieldName { get; set; }
        public string Message { get; set; }
    }

和如下所示的错误响应

public class ErrorResponse
    {
        public List<ErrorModel> Error { get; set; } = new List<ErrorModel>();
        public bool Successful { get; set; }
    }

答案 2 :(得分:0)

您可能想看看很棒的图书馆 FluentValidation

示例:

构建一个绑定您的 DTO 的验证器模块并创建一组规则。

public class CustomerValidator : AbstractValidator<Customer> {
  public CustomerValidator() {
    RuleFor(x => x.Surname).NotEmpty();
    RuleFor(x => x.Forename).NotEmpty().WithMessage("Please specify a first name");
    RuleFor(x => x.Discount).NotEqual(0).When(x => x.HasDiscount);
    RuleFor(x => x.Address).Length(20, 250);
    RuleFor(x => x.Postcode).Must(BeAValidPostcode).WithMessage("Please specify a valid postcode");
  }

  private bool BeAValidPostcode(string postcode) {
    // custom postcode validating logic goes here
  }
}

通过 DI 将其注入您的控制器:

    services.AddControllers()
                .AddFluentValidation(s =>
                {
                    s.ValidatorOptions.CascadeMode = CascadeMode.Stop;
                    s.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
                    s.ValidatorOptions.LanguageManager.Culture = new CultureInfo("en-US");
                    s.RegisterValidatorsFromAssemblyContaining<Customer>();
                    
                    ...
                    // more validators
                });
  • 这样您的代码看起来就井井有条;
  • 您摆脱了遍布代码的数据注释。
  • 个性化错误消息和验证。

您可能还想检查为什么在您的控制器上实现 ControllerBase 可能是使用 Web API 时要走的路。

答案 3 :(得分:0)

我刚刚更改了 http 方法并且可以很好地解决相同的问题

[HttpPost("认证")] public IActionResult Authenticate([FromBody]AuthenticateModel 模型)

答案 4 :(得分:0)

您想像这样输出 JSON:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|7ced8b82-4aa34d65daa99a12.",
    "errors": {
        "Vendor.UID": [
            "UID is required"
        ]
    }
}
  1. typestatus 字段,来自这里 https://github.com/dotnet/aspnetcore/blob/v3.1.17/src/Mvc/Mvc.Core/src/DependencyInjection/ApiBehaviorOptionsSetup.cs#L54-L108ClientErrorMapping 将在 dotnet 核心项目设置时配置。

  2. JSON 是由 ValidationProblemDetailshttps://github.com/dotnet/aspnetcore/blob/v3.1.17/src/Mvc/Mvc.Core/src/Infrastructure/DefaultProblemDetailsFactory.cs#L45

    创建的 DefaultProblemDetailsFactory
  3. 我们可以使用这个 ProblemDetailsFactory 来创建 ValidationProblemDetails https://github.com/dotnet/aspnetcore/blob/v3.1.17/src/Mvc/Mvc.Core/src/DependencyInjection/MvcCoreServiceCollectionExtensions.cs#L261