Wednesday, January 06, 2010

MVC Route Constraint to Exclude Values

For Album Credits I wanted to allow personalized urls of the format  This turned out to be quite an interesting routing exercise.

Since this is an MVC app, our standard url format is of the usual{controller}/{action}/{index} kind, and for some pages, I need to allow the url to simply specify the controller, defaulting the action to index – again, the usual ASP.NET MVC pattern.

I was familiar with the constraint parameter option for the AddRoute method, but had never studied it in much detail – we’d used it to limit certain indexes to be numeric, but that was all.  For the root-level personalized urls we needed a more robust constraint – specifically we needed to exclude any controller from the list of valid personalized Urls.

I first spent more time than I cared to trying to come up with a regular expression pattern that would NOT match the list of controller names – it looked something like this:
(thanks to Justin Poliey/ where foo and bar, etc were the controller names to NOT match.

Not until after I got that to work did I think to google “mvc custom route constraint”.  Of course the MS MVC team was smarter than that – custom route constraints are really very straight forward…

For my purposes, I went with David Hayden’s approach – the code below is essentially the same as his, just with the logic reversed.

using System;
using System.Linq;
using System.Web;
using System.Web.Routing;

namespace AlbumCredits.Web
	/// <summary>
	/// Route constraint that returns true if the parameter value is not one of the excluded values.
	/// </summary>
	/// <example>A controller constraint like 
	/// <code>new { controller = new ExcludeValuesConstraint("foo", "bar") }</code>
	/// will match "blah" or "snort" but will not match "foo" or "bar".
	/// </example>
	public class ExcludeValuesConstraint : IRouteConstraint
		private readonly string[] _excludeValues;
		/// <summary>
		/// Initializes a new instance of the <see cref="ExcludeValuesConstraint"/> class.
		/// Example: <code>new { controller = new ExcludeValuesConstraint("foo", "bar") }</code>
		/// will match "blah" or "snort" but will not match "foo" or "bar".
		/// </summary>
		/// <param name="excludeValues">The excluded values.</param>
		public ExcludeValuesConstraint(params string[] excludeValues)
			_excludeValues = excludeValues;

		/// <summary>
		/// Determines whether the URL parameter contains a valid value for this constraint.
		/// </summary>
		/// <param name="httpContext">An object that encapsulates information about the HTTP request.</param>
		/// <param name="route">The object that this constraint belongs to.</param>
		/// <param name="parameterName">The name of the parameter that is being checked.</param>
		/// <param name="values">An object that contains the parameters for the URL.</param>
		/// <param name="routeDirection">An object that indicates whether the constraint check is being performed when an incoming request is being handled or when a URL is being generated.</param>
		/// <returns>
		/// true if the URL parameter contains a valid value; otherwise, false.
		/// </returns>
		public bool Match(HttpContextBase httpContext, Route route, string parameterName, 
			RouteValueDictionary values, RouteDirection routeDirection)
			return !(_excludeValues.Contains(values[parameterName].ToString(), StringComparer.InvariantCultureIgnoreCase));

I can now use this when setting up my Route Table like this:

		/* for urls like  */ "{personalizedUrl}",
		/* route defaults */ new { controller = MVC.Profile.Name, action = MVC.Profile.Actions.IndexByPersonalizedUrl, personalizedUrl = string.Empty },
		/* where          */ new { personalizedUrl = new ExcludeValuesConstraint(ControllerNameArray) }

Labels: , , ,


  • How were you able to populate ControllerNameArray?

    By Blogger Andres, at Wednesday, January 27, 2010 3:45:00 AM  

  • Andres - that was a manual process. I guess I could have done something with a T4 template, or potentially the T4MVC generated code may already provide that, I don't know.

    In my case I used the T4MVC provided strongly typed names to create my array like this:

    return new string[] {
    MVC.About.Name, MVC.Admin.Name, MVC.Album.Name, MVC.Error.Name, MVC.Help.Name, MVC.Home.Name, ...

    By OpenID mo, at Friday, January 29, 2010 11:18:00 AM  

Post a Comment

Links to this post:

Create a Link

<< Home