How to make "One or more validation errors occurred" raise an exception?

15,242

Solution 1

You can add filter in your mvc service or controller service

this filter return badrequest

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

to create your filter you can add this class also you can customize this filter to what ever you want

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  
            }
        }
    }

I have created error model just like this

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

and error response like below

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

Solution 2

In order to achive this functionality you need to implement your own model validator described in this question: Model validation in Web API - Exception is thrown with out a throw statement?

Share:
15,242
AnKing
Author by

AnKing

Updated on June 21, 2022

Comments

  • AnKing
    AnKing over 1 year

    I'm running a WebAPI on Core 3.1 and one of my endpoints excepts JSON with a model that has fields with [Required] attribute like so:

    public class Vendor
    {
            public int ID { get; set; }
            [Required(ErrorMessage = "UID is required")]   <<<------ required attribute
            public string UID { get; set; }
    }
    

    When i call this endpoint without setting UID I get the following output as expected:

    {
        "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"
            ]
        }
    }
    

    Altho this output is pretty informative and clear it is not consistent with other error outputs that my API produces by means of ExceptionFilter. Is there any way this errors can be routed to the exception filter as well?

    Here is my Startup.cs:

    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" });
                });
            }
        }