Introduction
Have you ever been writing these codes all the time:
[HttpPost]
public IActionResult Create(Movie movie)
{
if (ModelState.IsValid)
{
return Json(..your data...);
}
return Json(..error...);
}
But if we can do something much better like:
[HttpPost]
public IActionResult Create(Movie movie)
{
return Json(..your data...);
}
Using the Code
When creating ASP.NET Core (.NET Core/.NET Framework) project, we choose Web API project template.
To implement global model validation, we implement these classes/interfaces:
ActionFilterAttribute
: Check ModelState.IsValid
and throw exception if there is any error
ModelStateDictionary
: Read all model errors
IExceptionFilter
: To handle global error
For the full source code, refer to my GitHub.
Implementing ActionFilterAttribute
:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class ValidateModelStateAttribute : ActionFilterAttribute
{
public ValidateModelStateAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
throw new ValidationException(context.ModelState.GetErrors());
}
}
}
Implementing ModelStateDictionary
:
public static class ModelStateExtensions
{
public static List GetErrors(this ModelStateDictionary modelState)
{
var validationErrors = new List();
foreach (var state in modelState)
{
validationErrors.AddRange(state.Value.Errors
.Select(error => error.ErrorMessage)
.ToList());
}
return validationErrors;
}
}
Implementing IExceptionFilter
:
public class HttpGlobalExceptionFilter : IExceptionFilter
{
public HttpGlobalExceptionFilter()
{
}
public void OnException(ExceptionContext context)
{
var exception = context.Exception;
var code = HttpStatusCode.InternalServerError;
var ajaxResponse = new AjaxResponse
{
IsSuccess = false
};
if (exception is ValidationException)
{
code = HttpStatusCode.BadRequest;
ajaxResponse.Message = "Bad request";
}
else
{
ajaxResponse.Message = "Internal Server Error";
}
context.Result = new JsonResult(ajaxResponse);
context.HttpContext.Response.StatusCode = (int)code;
context.ExceptionHandled = true;
}
}
Modifying ConfigureServices
method in Startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvc(options =>
{
options.Filters.Add(new ValidateModelStateAttribute());
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
})
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
}
Result
Now when methods are executed in controller classes, the validation process will be triggered and we can remove Model.IsValid
checking.
[Route("api/[controller]")]
public class MoviesController : Controller
{
[HttpPost]
public IActionResult Create([FromBody]Movie value)
{
return Json("OK");
}
}