Route Validation and Controller Validation in ASP.NET MVC

13 December 2012
by Dino Esposito

The ASP.NET MVC controller is a good friend of web developers. There are a host of features in it that can be used to reduce the amount of coding you have to do, and to make the logic simpler.

An ASP.NET MVC controller is a component that developers use as a tool to build their web applications. A controller has a long list of known capabilities and as many proven techniques for developers to use. As an ASP.NET MVC developer, you use the controller as the dispatcher for any server-side logic that you want to associate with an incoming request. In ASP.NET MVC, controllers run on top of the model binding layer and receive data that has been preprocessed into comfortable .NET types. You don’t even have the trouble of parsing parameters from the query string, form content or URL.

If all of this isn't enough to call an ASP.NET MVC controller a good friend of web developers, well, there’s way more to add. In this article, I’ll dig out some internals of MVC controllers and reveal some little known features that can sometimes be used to build powerful features.

As a disclaimer, let me say upfront that this article is built on top of some real experience. I faced a problem and I knew of a possible solution. On the way to that, though, I realized that other options were possible. Curiosity then hit me and I investigated further under the hood of ASP.NET MVC controllers. The result is this article where I touch on various levels of validation you have for URLs and also I’ll talk about giving controllers a “more” conventional behavior.

Facing the Problem

The problem I started from is overall a fairly common one: I’d even say that it is so common that many don’t even perceive it as a problem.

Suppose you have an application based on a standard URL scheme that fits well with the logic of the default route. Let’s say your URL takes the form of

{controller}/{action}/{id}

Route parameters such as controller and action can even be statically determined; they were actually static in the real scenario that I initially considered. In a nutshell, the problem is: how can you ensure that {id} gets only a well-known range of values? And if the route value is outside the allowed range of values, what’s the most appropriate way to handle it? Usually, with this type of problem, the range of values change frequently. In my original use-case the application had to be able to handle URLs like …

http://server/app/id1http://server/app/id2http://server/app/id3

…one week, and another set of IDs another week; rejecting URLs with the IDs of past weeks.

Analyzing Solutions

In ASP.NET MVC, this task seems to be ideally suited to a route constraint. Technically, a route constraint is a small piece of code that validates one parameter of a route. It can take the form of a regular expression or it can be a full-blown class where you can include logic that is more sophisticated. In another article of mine for Simple Talk, “Routing the ASP.NET Way”, I touched on both ways to express a route constraint. In this particular case, a regular expression is not an option. So let’s recap on what it means to have a route constraint class.

A route constraint class is a class that implements the IRouteConstraint interface:

public class StaticListRouteConstraint : IRouteConstraint
{
    bool Match(HttpContextBase httpContext,
                Route route,
                String parameterName,
                RouteValueDictionary values,
                RouteDirection routeDirection)
    {
        // Get the value of the parameter
        var paramValue = values[parameterName] as String;
        if (paramValue == null)
            return false;

        // Check the parameter value against the list
        ...
    }
}

The method Match is given access to the full HTTP context as well as additional route information in order to make a decision. In this particular case, there’s just one parameter to take into account: the effective value being passed which should be one of those listed on an external database or configuration file. You register a route constraint class as below:

routes.MapRoute(
  "Default",
  "{code}",
  new {controller="YourDefault", action="Index"},
  new {code = new StaticListRouteConstraint()}
);

With this trick switched on, a URL that has an invalid parameter just doesn’t match up the route. This leads straight to a more general problem.

What do you really need to do when you get a valid URL pattern but an invalid parameter? Is that something you really want to dismiss as just an invalid route? Or is it something that deserves more attention and a more detailed message to the user as well as action on your end?

There might be applications where a constraint that fails just identifies an invalid URL that can be served better by another route down the list. Likewise, there might be applications that need to stop the search for a valid route and just handle the validation and related view internally.

If you’re choosing the former option then, at the very minimum, you’ll want to have the canonical catch-all route at the end of the list:

routes.MapRoute(
    "CatchAll",
    "{*anything}",
    new { controller = "YourController", action = "Missing" }
);

Otherwise, the request will be matched and routed to the specified controller.

Route Validation vs. Controller Validation

ASP.NET MVC sometimes gives you several ways to accomplish the same task with the result that developers wonder what’s preferable or simply what should be preferable. Maybe because of the age, but I currently tend to be more than happy if I know at least one way of doing things. And, if it works, why looking elsewhere?

Route validation is effective because it can stop an invalid request quite early in the processing. At the same time, if you need more logic than a simple regular expression then you probably also need to access a database, and probably your logic may be quite sophisticated. My rule of thumb is go with a route constraint if you just want to rule out a URL pattern and have multiple patterns to take into account; accept the request and validate within the controller elsewhere. Once in a controller, you have no limits on what you can do; and you also have the chance to adjust the error view more comfortably. So my idea was to grab the parameter on a given action method, validate it and then decide what to do next.

The Unknown Action

It happened by pure coincidence, but it took me a few minutes to find it out. At some point, I ended up with a route configuration such that what I expected to be the {id} parameter of the default route was instead bound as the action. You can imagine the rest—an exception due to a missing action method on the controller class. (See Figure 1.)

The requested action is not found on the controller class

Figure 1: The requested action is not found on the controller class.

In my debugging efforts, I then remembered a feature I heard of a while back but never used in practice—a built-in handler for unknown actions invoked on a controller. To avoid the exception of Figure 1, one thing you can do (before trying to fix the route configuration if at all possible) is overriding the following method in the controller:

protected override void HandleUnknownAction(string actionName)
{
    base.HandleUnknownAction(actionName);
}

As you can see, your overridden member receives the name of the action as a string . The base implementation throws the aforementioned HttpException, but you can do whatever you want, including arranging a sort of catch-all net for user-defined actions.

As my problem was caused by an inaccurate route configuration, I actually had no need to override HandleUnknownAction to fix parameter values mistaken for action methods. However, it reminded me of a common scenario one could easily handle quite comfortably with HandleUnknownAction: take full control of invalid URL requests that are, for some reason, matched up before the catch-all route.

The idea is that you define a base controller class and place there the implementation of the HandleUnknownAction method. In this way, you are guaranteed that there’s an extra safety-net to protect your users from unwanted (and impolite) exceptions.

Conventional Controllers

When the application I was working on was nearly finished, I looked back at controller classes. It was actually a monolithic application with a very complex user interface but built around a single page: So I ended up with a couple of controllers generating views and one more controller acting as the API controller. I noticed that I had too many blocks of code like this:

public class MyController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
    public ActionResult DoThis()
    {
        return View();
    }
    public ActionResult DoThat()
    {
        return View();
    }
    ...
}

Each request generates a HTML template that is displayed to users and is then populated via script using libraries such as KnockoutJS for templating. Do I really need to have all that noisy code in the controller class?

I don’t have problems with lines of code, except when I see a clear and obvious purpose. If the purpose is obvious then probably the code can be pushed up in some higher level of abstraction. The question became: how can I have a fully functioning controller class without repetitive (but still necessary) code like above? Can something like the following be functional as well?

public class MyController : SomeMagicController
{
}

That was my challenge. And here’s the shortest answer I could come up with:

public class ConventionalController : Controller
{
    protected override void HandleUnknownAction(String actionName)
    {
        View().ExecuteResult(ControllerContext);
    }
}

View().ExecuteResult(ControllerContext);

And then having placed ConventionalController in a separate project (i.e., a distinct library) the application main controller class became:

public class MyController : ConventionalController
{
}

As you may imagine, there’s always code behind, not magic. The trick that makes this work is the safety-net represented by HandleUnknownAction. Every request sent out to a controller is processed by the action invoker component. This component ensures that the controller exposes a method that matches the action. If not, it invokes the HandleUnknownAction method. There’s no significant difference In performance between using the handle-unknown safety-net and going straight to an existing method.

The View method is implemented in the base Controller class. When it is invoked without parameters, it leaves to the view engine the burden of figuring out the default view name for the current request—view name matches the action name.

HandleUnknownAction is a void method so you can’t return anything out of it. This means that the implementation must take the extra step of actually executing the view in the current context. The point is that the action invoker takes a shortcut out of the usual workflow when it finds out that a non-existent action has been invoked. It basically tells the controller: ‘look, you’ve got a call for an action name that you don’t support; here’s your final chance to do something with it; if not, a HTTP exception will be thrown to interrupt the request’.

The job of the action invoker ends here with the default implementation of HandleUnknownAction. It is up to the controller to recover; but the controller is responsible for finding the view name as well as generating the markup. Of course, this works only with the assumption that a properly-named view exists.

Summary

The conventional controller can save you a few lines of boilerplate code and lets you focus on just writing the view and its related code. It’s not a trick that works in all scenarios; but I’ve found it effective in keeping the code even lighter in script-intensive web pages for relatively small-sized web sites.


© Simple-Talk.com