Clean up your Web API controllers with model validation and null check filters - StrathWeb

Strath

October 24th, 2012

Clean up your Web API controllers with model validation and null check filters

Because filters are your friends

Regardless of what kind of applications you build with Web API, you are bound to write a lot of similar code in many of your actions – to check if the model is valid and to check if the submitted object is null.

This creates unnecessary noise and repetition. In this short blog post, let’s have a quick at how you can delegate this kind of repetitive logic to Web API filters.

More after the jump.

The problem

In many of the MVC/Web API actions you are forced by the framework to do two types of checks – if the Model is valid or if it the model is not null.

I don’t think I have to spend too much time describing the problem. Let me show you a bit of code and you will immediately see what’s wrong here.

or

Dealing with the problem using custom filters

We can easily fix the redundancy in our controllers with two simple Web API filters. Remember – Web API, not MVC – so members of the System.Web.Http.Filters not System.Web.Mvc.Filters namespace.

Model validity

This is really simple – prior to executing the Web API controller, we peek into the ModelState dictionary, which contains collection of all errors and if it’s not valid we throw the status code 400 (bad request) back at client with the ModelState attached, as it wil contain the errors from our DataAnnotations or, for that matter, any other validation logic built around IValidateableObject.

Now we can see it in action, given a simple model:

I will post a completely invalid object, without a name and too long league property:

You can see we return a nice an informative message alongside error 400. You can obviously project to some sort of a view model to carry that information, instead of using ModelState directly but that’s really up to you.

Dealing with null checks

Another interesting problem is how to eliminate null checks. Obviously if we use Data Annotations and ModelState for complex objects, we can slightly eliminate the need to check for null, but not always.

The problem is if you submit, for example an empty JSON, ModelState would kick in and model will be invalid -all good. But if you submit a completely empty request, the ModelState will be treated as valid (!) and null value passed to the controller which, without a null check, would likely throw an exception!

Moreover, with primitive input (i.e. string, int) we don’t even have data annotations, and then we have to rely on null check.

We can solve the problem by writing another filter:

This is only slightly more complex. We accept a single input paramter when the argument is declared – a Func to provide a plumbing mechanism for custom logic. By default we simply check – if the argument is null we invalidate the request.

I set the attribute usage to allow inheritance, since this type of attribute you might consider putting i.e. on an abstract base class (you might, for exmple, have the base RESTController class which defines CRUD operations etc).

Now, to use it you simply declare the attribute and try sending a null value:

As expected, the request is rejected:

Notice I used nullable int – as otherwise a null would turn into a value 0.

The same will happen with complex types or strings – the filter can also be applied to them. A good example is if we post a null to an action that requires a Team object, like we did before.

Now we are super protected:

If you wish, you can register the filters globally to avoid having to declare them each time:

Summary

With these two simple filters you can make your life much, much simpler and your Web API controllers code cleaner. MVC and now Web API framework do a great job at providing us developers with plumbing mechanisms for plugging in common utilities and validation checks that otherwise would have to be repeated inside of controllers.

And you know, the cleaner the controllers, the easier it is to manage your application.

Be Sociable, Share!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1217

  • Pingback: Dew Drop – October 24, 2012 (#1,428) | Alvin Ashcraft's Morning Dew

  • Cool

    Thank you for your blog.Thanks Again. Great.

  • http://about.me/IDisposable Marc Brooks

    Would this be the MVC 4 (not WebAPI) equivalent?

    public class ValidateModelStateAttribute : ActionFilterAttribute
    {
    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
    var controller = actionContext.Controller as Controller;
    if (controller != null && !controller.ModelState.IsValid)
    {
    actionContext.Result = new JsonResult { Data = controller.ModelState };
    }
    }
    }

    • http://about.me/IDisposable Marc Brooks

      Doh! That left out the response code!

      • http://about.me/IDisposable Marc Brooks

        Insert before .Result line
        controller.Response.StatusCode = (int)HttpStatusCode.BadRequest;

  • http://about.me/IDisposable Marc Brooks

    And this seems to be right for MVC 4 version of the null check.

    public static partial class IDictionaryExtensions
    {
    public static bool ContainsValue(this IDictionary dict, TValue value)
    {
    var comparer = EqualityComparer.Default;
    return dict.Any(pair => comparer.Equals(pair.Value, value));
    }
    }

    [AttributeUsage(AttributeTargets.Method, Inherited = true)]
    public class CheckModelForNullAttribute : ActionFilterAttribute
    {
    private readonly Func<IDictionary, bool> _validate;

    public CheckModelForNullAttribute()
    : this(arguments =>
    arguments.ContainsValue(null))
    { }

    public CheckModelForNullAttribute(Func<IDictionary, bool> checkCondition)
    {
    _validate = checkCondition;
    }

    public override void OnActionExecuting(ActionExecutingContext actionContext)
    {
    var controller = actionContext.Controller as Controller;

    if (controller != null && _validate(actionContext.ActionParameters))
    {
    controller.Response.StatusCode = (int)HttpStatusCode.BadRequest;
    controller.Response.StatusDescription = “The argument cannot be null”;
    actionContext.Result = new JsonResult { Data = controller.ModelState };
    }
    }
    }

    • Filip W

      yeah looks good!
      very nice :)

  • Vince

    I didn’t think it was possible to pass in a lambda expression to an attribute constructor…

  • Pingback: .Net News – Oktober Summary – Namics Weblog

  • http://wallaceturner.com Wallace Turner

    Thanks for this. Can I suggest adding the name(s) of the null arguments to the error message:
    var nullArgs = actionContext.ActionArguments.Where(kv => kv.Value == null).Select(kv => string.Format(“The argument ‘{0}’ cannot be null”, kv.Key)).ToArray();
    if(nullArgs.Any())
    {
    actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, string.Join(“\n”, nullArgs));
    }

    Also, can you please make your comments section wider and add a markdown editor so code looks nice. :)

  • http://weblogs.asp.net/imranBaloch/ Imran Baloch

    You can easily combine both to avoid more repetition.

  • http://www.calabonga.net/ Calabonga

    Thanks! Very useful!!!

  • Jeffrey Soper

    Can you expand on your initial example with a primitive parameter: public void Post(string text) { // … }. How would you do more than check for a null string in an ActionFilterAttribute? Suppose you wanted to ensure the string was also between 5-20 chars long, contained at least one ‘^’ char, began with the word ‘BLUE’, was all uppercase, etc. How could you check that all of these requirements were met, and embed a meaningful message for each different validation error? What if you had more than one parameter, each with its own validation rules – would you have a separate attribute class for each, or select them individually from actionContext.ActionArguments?