Table of Contents
Issue
I need custom message for my request.
Currently I am passing below request json from postman
{
"countryid": "14sdsads02"
}
but my model is
public class model
{
public int countryid {get;set;}
}
When pass request from postman, I am getting below error
{
"type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "|21793cf-495c68ddbc92ab35.",
"errors": {
"$.countryid": [
"The JSON value could not be converted to System.Int32. Path: $.countryid | LineNumber: 1 | BytePositionInLine: 29."
]
}
}
But instead of this, I need custom error in my web api. How can I achieve this?
Solution
If you want to define a custom response in case of 400 then you can do the followings:
The handler
In order to catch the model binding errors you have to wire up a handler for the ApiBehaviorOptions
‘s InvalidModelStateResponseFactory
(1).
The required delegate is quite generic: Func<ActionContext, IActionResult>
. So, first let’s create an interface for this:
public interface IModelBindingErrorHandler
{
IActionResult HandleInvalidModelState(ActionContext context);
}
Here is a simple handler implementation:
public class ModelBindingErrorHandler : IModelBindingErrorHandler
{
private ICorrelationContextAccessor correlationContextAccessor;
private ILogger logger;
public ModelBindingErrorHandler(ICorrelationContextAccessor correlationContextAccessor, ILogger logger)
{
this.correlationContextAccessor = correlationContextAccessor;
this.logger = logger;
}
public IActionResult HandleInvalidModelState(ActionContext context)
{
string correlationId = correlationContextAccessor.CorrelationContext?.CorrelationId;
var modelErrors = context.ModelState
.Where(stateEntry => stateEntry.Value.Errors.Any())
.Select(stateEntry => new InvalidData
{
FieldName = stateEntry.Key,
Errors = stateEntry.Value.Errors.Select(error => error.ErrorMessage).ToArray()
});
logger.LogError("The request contained malformed input.", modelErrors);
var responseBody = new GlobalErrorModel
{
ErrorMessage = "Sorry, the request contains invalid data. Please revise.",
ErrorTracingId = correlationId
};
return new BadRequestObjectResult(responseBody);
}
}
The helper classes
public class GlobalErrorModel
{
public string ErrorMessage { get; set; }
public string ErrorTracingId { get; set; }
}
public class InvalidData
{
public string FieldName { get; set; }
public string[] Errors { get; set; }
public override string ToString() => $"{FieldName}: {string.Join("; ", Errors)}";
}
Self-registration
public static class ModelBindingErrorHandlerRegister
{
/// <summary>
/// This method should be called after <b>AddControllers();</b> call.
/// </summary>
public static IServiceCollection AddModelBinderErrorHandler(this IServiceCollection services)
{
return AddModelBinderErrorHandler<ModelBindingErrorHandler>(services);
}
/// <summary>
/// This method can be used to register a custom Model binder's error handler.
/// </summary>
/// <typeparam name="TImpl">A custom implementation of <see cref="IModelBindingErrorHandler"/></typeparam>
public static IServiceCollection AddModelBinderErrorHandler<TImpl>(this IServiceCollection services)
where TImpl : class, IModelBindingErrorHandler
{
services.AddSingleton<IModelBindingErrorHandler, TImpl>();
var serviceProvider = services.BuildServiceProvider();
var handler = serviceProvider.GetService<IModelBindingErrorHandler>();
services.PostConfigure((ApiBehaviorOptions options) =>
options.InvalidModelStateResponseFactory = handler.HandleInvalidModelState);
return services;
}
}
Usage
public partial class Startup
{
private readonly IConfiguration Configuration;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers()
.AddNewtonsoftJson();
//Omited for brevity
services.AddModelBinderErrorHandler();
}
//Omited for brevity
}
Answered By – Peter Csala
Answer Checked By – Dawn Plyler (BugsFixing Volunteer)