andrewlocatelliwoodcock

Thoughts on Software

Canceling the ActionExecutingContext in the OnActionExecuting ActionFilter

with 5 comments


I have been working recently with ActionFilters in ASP.Net MVC and was trying to prevent processing of a Controller action method from within the OnActionExecting action filter. As usual, MVC supports this level of customization, but it was not immediately obvious how to achieve it.

Specifically, I don’t like the boiler plate code required by the standard approach to only processing an action method if the model is valid and I wanted to replace it with a custom action filter instead.

The standard approach is something like this:

   [HttpPost]
   public JsonResult Checkout(CheckoutModel model)
   {
      if (ModelState.IsValid)
      {
         // model is valid, so do something here and return result
         ...
         return this.Json(id);
      }
      // model is not valid, so return something else!
      return new JsonResult();
   }

 

As you can see, this requires each action method to have a piece of boiler plate code to wrap the action code in an if statement: if (ModelState.IsValid). For each and every method in which you want to check model validity before continuing. Ugh! C’mon MVC, you can do better than that! So, the idea for a validating action filter was born in which we would check model validity and prevent further processing if invalid. Actually, I want to do a lot more than that but that’s beyond the scope of this post.

What I wanted was something like this:

   [HttpPost]

[OnlyProcessIfValid]

   public JsonResult Checkout(CheckoutModel model)
   {
      // we can guarantee that model is valid at this point
      // so do our normal processing
      ...
      return this.Json(id);
   }

OnlyProcessIfValid is a custom action filter. For each action filter, there are four events which fire in the following order:

  1. OnActionExecuting
  2. OnActionExecuted
  3. OnResultingExecuting
  4. OnResultExecuted

OnResultingExecuting and OnResultExecuted both provide a Cancel property for their respective filter context objects which cancels further processing. OnActionExecuted provides a Cancelled property for the same purpose. But these three are too late in the processing cycle for my purposes: I wanted to stop the action method during OnActionExecuting if the model was invalid before any processing has occurred within the actual controller action method. And guess what? There’s no Cancel or Cancelled property available!

MVC, being the outstanding web framework it is, does of couse provide a mechanism to do this but whilst it is easy to implement, it is not immediately obvious: you need simply set the filterContext.Result to a non-null value and that effectively cancels further processing:

using System.Web.Mvc;
namespace LocatelliWoodcock.Controllers
{
   public class OnlyProcessIfValidAttribute: ActionFilterAttribute, IActionFilter
   {

      void IActionFilter.OnActionExecuted(ActionExecutedContext filterContext)
      {
         // not interested in this as it's too late in the processing cycle!
      }

      void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
      {
         if (!filterContext.Controller.ViewData.ModelState.IsValid)
         {
            // cancel processing now! Do this by setting Result to a non-null value, in this example a JsonResult
            filterContext.Result = new JsonResult();
         }
      }

}

}

 

Simples!

Written by andrewlocatelliwoodcock

December 15, 2011 at 12:24

5 Responses

Subscribe to comments with RSS.

  1. […] method that is being called, we have access to a validated model. In fact, as discussed in my last post, we actually have access to the results of model validation in the method’s ActionFilters, […]

  2. We are a group of volunteers and opening a new scheme in our community. Your website offered us with valuable info to work on. You have done an impressive job and our entire community will be thankful to you.

    Seth Bowery

    December 29, 2011 at 11:15

  3. I’d should examine with you here. Which is not something I normally do! I get pleasure from reading a post that may make folks think. Additionally, thanks for permitting me to remark!

    Donnell Cavalaris

    January 6, 2012 at 15:04

  4. Hello,
    Why don’t you avoid the server call by a JQuery routine client side ?
    In your case, how will you treat the result of your server call if the filter avoid the method code ?
    Thank’s for your answer
    Best regards,
    Laurent.

    Laurent

    May 18, 2012 at 14:15

    • Hi Laurent,

      Basically, in this scenario, we did actually use JQuery-based validation on the client-side, however best practice is always to perform validation on both client- and server-side. The reason for this is that you cannot ever guarantee that the client-side code has actually run (someone could have disabled JavaScript in their browser) or that the JS has not been compromised. For that reason, you must always check again on the server-side. We still perform client-side scripting because in (hopefully) the majority of cases the JS runs and is uncompromised and performing the validation client-side avoids unnecessary round-trips to the server.

      There is also another reason for performing server-side validation – some of the validation we are performing is based on user permissions which is information we never want to pass to the client-side for security purposes.

      You need to handle the scenario where validation fails on the client. In our live system, we returned error information to the client describing how and why validation failed. This was done in a format that matched what JQuery validation was expecting and so we were able to display it on screen in exactly the same way as validation failures discovered by client-side validation code.

      Andrew

      andrewlocatelliwoodcock

      May 21, 2012 at 19:20


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: