Custom Validation of Model Properties via Attributes and Filters in Asp.Net Core

Georg Dangl by Georg Dangl Monday, December 19, 2016

Monday, December 19, 2016

Whenever you offer services that might cause user lockout, you're well advised to add a bit of effort to make accidental errors less probable. That's what confirm password fields are for when you sign up or change passwords, and they're used on virtually any site that has the slightest user interaction. This post is focusing on setting this up on the client side in Asp.Net Core.

First, you'll need to use the CompareAttribute from the System.ComponentModel.DataAnnotation namespace (here's the source on GitHub) to indicate that you want one of your models properties to equal another one. It's best to supply the property name via the nameof() expression, so you'll get a compile-time constant and still benefit from refactoring if you need to:

using System.ComponentModel.DataAnnotations;

namespace ViewModels
{
    public class CreateUserPost
    {
        public string Username { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        [Compare(nameof(Password))]
        public string ConfirmPassword { get; set; }
    }
}

This will invalidate your ModelState whenever Password and ConfirmPassword don't match, without requiring you to do extra comparisons in your controller. To go further, you don't even need to check for the ModelState at all in your controllers by putting the validation logic in a simple IActionFilter that's executed before every request:

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace Filters
{
    public class ApiModelStateValidationFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid && context.HttpContext.Request.Path.StartsWithSegments("/Api", StringComparison.OrdinalIgnoreCase))
            {
                context.Result = new BadRequestObjectResult(context.ModelState);
            }
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // Not doing anything after the action has executed
        }
    }
}

The filter is added to the Mvc configuration in the Startup class:

services.AddMvc(options =>
{
    options.Filters.Add(typeof(ApiModelStateValidationFilter));
});

Invalid requests are now being captured before they even reach your controller and give meaningful error messages:

{
    "errors": {
        "ConfirmPassword": [
            "'ConfirmPassword' and 'Password' do not match."
        ]
}

Happy validating!


Share this post


comments powered by Disqus

About me

Hi, my name's George! I love coding and blogging about it. I focus on all things around .Net, Web Development and DevOps.

DanglIT

Need a consultant for BIM, GAEB or Software Development?

Contact me at [email protected], +49 (173) 56 45 689 or visit my professional page!

Dangl.Blog();
// Just 💗 Coding

Social Links