Skip to main content

Blog

Home
About Us
What We Do
ContactUs
Blog
Go Search
Other Blogs
There are no items in this list.
Home > Blog 

Include and Exclude Constraints in ASP.NET MVC

Otherwise known as a white list or black list for route tokens, this simple IRouteConstraint is coming in handy and I thought i would share:

  1. public enum ListConstraintType {
  2.     Exclude,
  3.     Include
  4. }
  5. public class ListConstraint : IRouteConstraint {
  6.     public ListConstraintType ListType { get; set; }
  7.     public IList<string> List { get; set; }
  8.     public ListConstraint() : this(ListConstraintType.Include, new string[] { }) { }
  9.     public ListConstraint(params string[] list) : this(ListConstraintType.Include, list) { }
  10.     public ListConstraint(ListConstraintType listType, params string[] list) {
  11.         if (list == null) throw new ArgumentNullException("list");
  12.         this.ListType = listType;
  13.         this.List = new List<string>(list);
  14.     }
  15.     public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
  16.         if (string.IsNullOrEmpty(parameterName)) throw new ArgumentNullException("parameterName");
  17.         if (values == null) throw new ArgumentNullException("values");
  18.         string value = values[parameterName.ToLower()] as string;
  19.         bool found = this.List.Any(s => s.Equals(value, StringComparison.OrdinalIgnoreCase));
  20.         return this.ListType == ListConstraintType.Include ? found : !found;
  21.     }
  22. }

You can then use the ListConstraint like this:

  1. public static void RegisterRoutes(RouteCollection routes) {
  2.     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  3.     routes.IgnoreRoute("content/{*pathInfo}");
  4.     routes.IgnoreRoute("favicon.ico");
  5.     routes.MapRoute(
  6.         "UserShortcuts",
  7.         "{action}/{id}",
  8.         new { controller = "user", id = "" },
  9.         new { action = new ListConstraint(ListConstraintType.Include, "signin", "signout") }
  10.     );
  11.     routes.MapRoute(
  12.         "HomeShortcuts",
  13.         "{action}",
  14.         new { controller = "home" },
  15.         new { action = new ListConstraint("sitemap", "contact") } //default Include
  16.     );
  17.     routes.MapRoute(
  18.         "Default",
  19.         "{controller}/{action}/{id}",
  20.         new { controller = "home", action = "index", id = "" },
  21.         new { id = new ListConstraint(ListConstraintType.Exclude, "123") }
  22.     );
  23. }
  24. protected void Application_Start() {
  25.     RegisterRoutes(RouteTable.Routes);
  26. }

Which then allows only those included constraints to use the UserShortcuts route:

Or prevents the id of 123 from being used on the Default route:

 

Hope folks find this useful,

Jason Conway

Aliasing and Localizing Routes in ASP.NET MVC

I had come across a forum post where someone wanted to localize urls in the RouteTable and I thought I would come up with my first stab at a better solution.  What I discovered turned out to be a pretty simple way of what I am calling ‘Aliasing Routes’ where a route token can have an alias value as a localized value.

DOWNLOAD – My entire MvcSamples VSTS solution (complete with Moq and 100% code coverage)

This is how you do it (the complete source is available for download above):

Step 1: Persistence – we need a way to store the aliases, so I created a custom ConfigurationSection (an external file or database would also work) that looks like this when complete:

  1. <aliasRoutes>
  2.   <routes>
  3.     <add token="controller" value="home" aliases="inicio, beginning, baile, start" />
  4.     <add token="controller" value="about" aliases="info, sobre, more, additional" />
  5.     <add token="action" value="signin" aliases="login, logon, ingresar, anmelden" />
  6.   </routes>
  7. </aliasRoutes>

Step 2: Module – we need a way to inspect route data and change route values when applicable, so I derived from UrlRoutingModule that looks like this when complete:

  1. public class AliasRoutingModule : UrlRoutingModule {
  2.     protected static readonly object itemsKey = new object();
  3.     private class RequestInfo {
  4.         public string OriginalPath { get; set; }
  5.         public IHttpHandler HttpHandler { get; set; }
  6.     }
  7.     public override void PostMapRequestHandler(HttpContextBase context) {
  8.         Guard.AgainstNullParameter(context, "context");
  9.         RequestInfo info = context.Items[itemsKey] as RequestInfo;
  10.         if (info == null) return;
  11.         context.RewritePath(info.OriginalPath);
  12.         context.Handler = info.HttpHandler;
  13.     }
  14.     public override void PostResolveRequestCache(HttpContextBase context) {
  15.         Guard.AgainstNullParameter(context, "context");
  16.         RouteData routeData = this.RouteCollection.GetRouteData(context);
  17.         if (routeData == null) return;
  18.         IRouteHandler routeHandler = routeData.RouteHandler;
  19.         if (routeHandler == null) throw new InvalidOperationException("No RouteHandler identified");
  20.         if (routeHandler is StopRoutingHandler) return;
  21.         IDictionary<string, object> values = new Dictionary<string, object>(routeData.Values);
  22.         AliasRoutesSection section = SectionRepository.Current.AliasRoutes;
  23.         //*** magic happens here
  24.         foreach (string token in values.Keys) {
  25.             string alias = values[token].ToString();
  26.             string value = section.Routes.GetValue(token, alias);
  27.             if (value != null) routeData.Values[token] = value;
  28.         }
  29.         RequestContext requestContext = new RequestContext(context, routeData);
  30.         IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
  31.         if (httpHandler == null) throw new InvalidOperationException("No HttpHandler identified");
  32.         RequestInfo info = new RequestInfo {
  33.             OriginalPath = context.Request.Path,
  34.             HttpHandler = httpHandler
  35.         };
  36.         context.Items[itemsKey] = info;
  37.         context.RewritePath("~/UrlRouting.axd");
  38.     }
  39. }

Step 3: Web Config – need to change our UrlRoutingModule registration in the web.config to the new AliasRoutingModule:

  1.   <httpModules>
  2.     <add name="ScriptModule"
  3.          type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  4.     <add name="AliasRoutingModule"
  5.          type="Salient6.MvcSamples.Web.Modules.AliasRoutingModule"/>
  6.   </httpModules>
  7. </system.web>

Step 4: RegisterRoutes – our Global stays clean and manageable:

  1. public static void RegisterRoutes(RouteCollection routes) {
  2.     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  3.     routes.IgnoreRoute("content/{*pathInfo}");
  4.     routes.IgnoreRoute("favicon.ico");
  5.     routes.MapRoute(
  6.         "Default",                                              
  7.         "{controller}/{action}/{id}",                           
  8.         new { controller = "home", action = "index", id = "" }
  9.     );
  10. }

Step 5: Browser – we should be able to get to the Home Controller now by navigating to /start:

Alias Route Start

OR something way cooler like /inicio/anmelden  which is a combination of Controller and Action aliases!

Alias Route Start

What’s nice about this is you can alias and/or localize your url without having to MapRoute, you can make changes in the config without having to recompile, and you can mix and match aliases as long as they resolve to a valid endpoint!

Anyway, I hope folks find this useful!

Jason Conway

kick it on DotNetKicks.com

Include and Exclude Constraints in ASP.NET MVC

Otherwise known as a white list or black list for route tokens, this simple IRouteConstraint is coming in handy and I thought i would share:

  1. public enum ListConstraintType {
  2.     Exclude,
  3.     Include
  4. }
  5. public class ListConstraint : IRouteConstraint {
  6.     public ListConstraintType ListType { get; set; }
  7.     public IList<string> List { get; set; }
  8.     public ListConstraint() : this(ListConstraintType.Include, new string[] { }) { }
  9.     public ListConstraint(params string[] list) : this(ListConstraintType.Include, list) { }
  10.     public ListConstraint(ListConstraintType listType, params string[] list) {
  11.         if (list == null) throw new ArgumentNullException("list");
  12.         this.ListType = listType;
  13.         this.List = new List<string>(list);
  14.     }
  15.     public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) {
  16.         if (string.IsNullOrEmpty(parameterName)) throw new ArgumentNullException("parameterName");
  17.         if (values == null) throw new ArgumentNullException("values");
  18.         string value = values[parameterName.ToLower()] as string;
  19.         bool found = this.List.Any(s => s.Equals(value, StringComparison.OrdinalIgnoreCase));
  20.         return this.ListType == ListConstraintType.Include ? found : !found;
  21.     }
  22. }

You can then use the ListConstraint like this:

  1. public static void RegisterRoutes(RouteCollection routes) {
  2.     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  3.     routes.IgnoreRoute("content/{*pathInfo}");
  4.     routes.IgnoreRoute("favicon.ico");
  5.     routes.MapRoute(
  6.         "UserShortcuts",
  7.         "{action}/{id}",
  8.         new { controller = "user", id = "" },
  9.         new { action = new ListConstraint(ListConstraintType.Include, "signin", "signout") }
  10.     );
  11.     routes.MapRoute(
  12.         "HomeShortcuts",
  13.         "{action}",
  14.         new { controller = "home" },
  15.         new { action = new ListConstraint("sitemap", "contact") } //default Include
  16.     );
  17.     routes.MapRoute(
  18.         "Default",
  19.         "{controller}/{action}/{id}",
  20.         new { controller = "home", action = "index", id = "" },
  21.         new { id = new ListConstraint(ListConstraintType.Exclude, "123") }
  22.     );
  23. }
  24. protected void Application_Start() {
  25.     RegisterRoutes(RouteTable.Routes);
  26. }

Which then allows only those included constraints to use the UserShortcuts route:

Or prevents the id of 123 from being used on the Default route:

 

Hope folks find this useful,

Jason Conway

Aliasing and Localizing Routes in ASP.NET MVC

I had come across a forum post where someone wanted to localize urls in the RouteTable and I thought I would come up with my first stab at a better solution.  What I discovered turned out to be a pretty simple way of what I am calling ‘Aliasing Routes’ where a route token can have an alias value as a localized value.

DOWNLOAD – My entire MvcSamples VSTS solution (complete with Moq and 100% code coverage)

This is how you do it (the complete source is available for download above):

Step 1: Persistence – we need a way to store the aliases, so I created a custom ConfigurationSection (an external file or database would also work) that looks like this when complete:

  1. <aliasRoutes>
  2.   <routes>
  3.     <add token="controller" value="home" aliases="inicio, beginning, baile, start" />
  4.     <add token="controller" value="about" aliases="info, sobre, more, additional" />
  5.     <add token="action" value="signin" aliases="login, logon, ingresar, anmelden" />
  6.   </routes>
  7. </aliasRoutes>

Step 2: Module – we need a way to inspect route data and change route values when applicable, so I derived from UrlRoutingModule that looks like this when complete:

  1. public class AliasRoutingModule : UrlRoutingModule {
  2.     protected static readonly object itemsKey = new object();
  3.     private class RequestInfo {
  4.         public string OriginalPath { get; set; }
  5.         public IHttpHandler HttpHandler { get; set; }
  6.     }
  7.     public override void PostMapRequestHandler(HttpContextBase context) {
  8.         Guard.AgainstNullParameter(context, "context");
  9.         RequestInfo info = context.Items[itemsKey] as RequestInfo;
  10.         if (info == null) return;
  11.         context.RewritePath(info.OriginalPath);
  12.         context.Handler = info.HttpHandler;
  13.     }
  14.     public override void PostResolveRequestCache(HttpContextBase context) {
  15.         Guard.AgainstNullParameter(context, "context");
  16.         RouteData routeData = this.RouteCollection.GetRouteData(context);
  17.         if (routeData == null) return;
  18.         IRouteHandler routeHandler = routeData.RouteHandler;
  19.         if (routeHandler == null) throw new InvalidOperationException("No RouteHandler identified");
  20.         if (routeHandler is StopRoutingHandler) return;
  21.         IDictionary<string, object> values = new Dictionary<string, object>(routeData.Values);
  22.         AliasRoutesSection section = SectionRepository.Current.AliasRoutes;
  23.         //*** magic happens here
  24.         foreach (string token in values.Keys) {
  25.             string alias = values[token].ToString();
  26.             string value = section.Routes.GetValue(token, alias);
  27.             if (value != null) routeData.Values[token] = value;
  28.         }
  29.         RequestContext requestContext = new RequestContext(context, routeData);
  30.         IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
  31.         if (httpHandler == null) throw new InvalidOperationException("No HttpHandler identified");
  32.         RequestInfo info = new RequestInfo {
  33.             OriginalPath = context.Request.Path,
  34.             HttpHandler = httpHandler
  35.         };
  36.         context.Items[itemsKey] = info;
  37.         context.RewritePath("~/UrlRouting.axd");
  38.     }
  39. }

Step 3: Web Config – need to change our UrlRoutingModule registration in the web.config to the new AliasRoutingModule:

  1.   <httpModules>
  2.     <add name="ScriptModule"
  3.          type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
  4.     <add name="AliasRoutingModule"
  5.          type="Salient6.MvcSamples.Web.Modules.AliasRoutingModule"/>
  6.   </httpModules>
  7. </system.web>

Step 4: RegisterRoutes – our Global stays clean and manageable:

  1. public static void RegisterRoutes(RouteCollection routes) {
  2.     routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
  3.     routes.IgnoreRoute("content/{*pathInfo}");
  4.     routes.IgnoreRoute("favicon.ico");
  5.     routes.MapRoute(
  6.         "Default",                                              
  7.         "{controller}/{action}/{id}",                           
  8.         new { controller = "home", action = "index", id = "" }
  9.     );
  10. }

Step 5: Browser – we should be able to get to the Home Controller now by navigating to /start:

Alias Route Start

OR something way cooler like /inicio/anmelden  which is a combination of Controller and Action aliases!

Alias Route Start

What’s nice about this is you can alias and/or localize your url without having to MapRoute, you can make changes in the config without having to recompile, and you can mix and match aliases as long as they resolve to a valid endpoint!

Anyway, I hope folks find this useful!

Jason Conway

kick it on DotNetKicks.com

 ‭(Hidden)‬ Admin Links

 Recent Posts

Include and Exclude Constraints in ASP.NET MVC
Aliasing and Localizing Routes in ASP.NET MVC