Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ASP.NET Core Global Model Validation

0.00/5 (No votes)
1 Aug 2017 1  
In this article, you will learn about ASP.NET Core global model validation

Introduction

Have you ever been writing these codes all the time:

[HttpPost]
public IActionResult Create(Movie movie)
{
    if (ModelState.IsValid)
    {
        // Do something
        // ...
        return Json(..your data...);
    }
    return Json(..error...);
}

But if we can do something much better like:

[HttpPost]
public IActionResult Create(Movie movie)
{
    // Do something
    // ...
    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)
            {
                // When ModelState is not valid, we throw exception
                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";
            // We can serialize exception message here instead of throwing Bad request message
        }
        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
{
    // POST api/movies
    [HttpPost]
    public IActionResult Create([FromBody]Movie value)
    {
        return Json("OK");
    }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here