Routing

.ASP.NET Routing

Routing

Web page by Kevin Harris of Homer IL

Please contact Kevin Harris of Homer IL concerning this web site

"ASP.NET routing enables you to use URLs that do not have to map to specific files in a Web site, making more easily understood URLs that are descriptive of the page's contents."


Default implementations of Web Forms applications contain a request URL which specifies a physical .aspx file. The .aspx file contains the code for obtaining data and formatting a screen. However, default implementations of ASP.NET MVC applications use a routing engine to map URL patterns to action methods inside a controller. The action methods contain the logic to direct the process of obtaining the data and passing it to a view for display. Routing allows request URLs to contain meaningful information about the requested page.

For example, a non-routed URL may appear as:

https://store.com/wines.aspx?id=762

while a routed URL may appear as:

https://store.com/wines/red/merlot

The meaningful information in routed URL make it easier for users to guess how to change the URL to get to the pages they desire. Also, search engines will rate a page higher when it contains meaningful information in the URL.


Route Collections

The routes a routing engine uses is stored in a RouteCollection data structure. Routes are added to the route collection in the RegisterRoutes method of the RouteConfig class. A default route is created when the MVC project is created. The default route can be modified or additional routes can be created as desired. The routes.MapRoute method is used to define a route. The order in which the routes are defined in the RegisterRoutes method is significant. The first route which matches the URL pattern will direct the request to an action method. Any routes defined after the matched route will be ignored by the request. So the URL patterns have to be designed so the request URLs flow down the list routes to find the appropriate action method to invoke. Below are a custom route followed by the MVC default route defined in RouteConfig.cs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Web.Mvc;
using System.Web.Routing;

namespace CathySite.Web
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute("ArticleArchive",
                "{year}/{month}/{day}",
                new { controller = "Article", action = "List", month = "1", day = "1" },
                new { year = @"\d{2}|\d{4}", month = @"\d{1,2}", day = @"\d{1,2}" }
                );

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
}


Routes Defined in RouteConfig.cs

Defining Routes

To add customer routes, Web Forms applications use the MapPageRoute method of the RouteCollection class, while MVC applications use the MapRoute method. This article is only going to cover adding routes to MVC applications. For web forms, see this article: Adding Routes to a Web Forms Application.

MapRoute is an extension method on the RouteCollection class. MapRoute has a number of overloads. The MapRoute overload with the most parameters is shown below.

1
2
3
4
5
6
7
8
public static Route MapRoute(
	this RouteCollection routes,
	string name,
	string url,
	object defaults,
	object constraints,
	string[] namespaces
)


MapRoute Extension Method

The parameters for MapRoute include:

  1. name - the name of the route to map.

  2. url - the URL pattern for the route.

  3. defaults - an object that contains default route values.

  4. constraints - set of expressions that limits acceptable values for a URL. An example would be:

    new { id = @"\d+" } // Constraint to Only Allow Numeric IDs

    Note: Route constraints can also be added when the route is registered in the global.asax, such as:

    reportRoute.Constraints = new RouteValueDictionary { { "locale", "[a-z]{2}-[a-z]{2}" }, { "year", @"\d{4}" } };

  5. namespaces - used with Areas, to specify the area namespace(s) of the desired controller.

Phil Haack created a Route Debugger that can be installed through NuGet (PM> Install-Package routedebugger) which shows defined routes in an application and the route which matches the entered URL.

.Route Debugger


Routing Path

Request Processing Using Routing

Request first pass through the UrlRoutingModule HTTP Module. UrlRoutingModule contains the logic for the routing engine. WHen it determines a request URL matches a defined pattern, it passes the URL to the MVCRouterHandler IHTTP Handler which creates an MvcHandler. The MvcHandler finds the controller component in the URL and passes it to IControllerFactory which creates the controller which implement the IController interface. MvcHandler is delegating the processing of URL request to a controller. MvcHandler is responsible for creating the controller (via IControllerFactory) and cleaning up after the controller has finished.

The IController interface is the lowest level abstraction which only implements an execute method. Richer abstractions include a ControllerBase class which includes properties for passing data to a view. Derived from the ControllerBase class is the Controller class which contains actions. It is from the Controllers class that custom controllers are typically derived.

.Routing Path


Routing Path


Custom Route Handlers

Custom route handlers can be created for specialized routing needs which can not be handled by the default route handler. The following code is an example of a custom route handler.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System.Web;
using System.Web.Routing;

namespace CustomRouteHandler
{
    public class CustomRouteHandler: IRouteHandler
    {
        private string _message;
        public CustomRouteHandler(string message)
        {
            _message = message;
        }

        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new CustomHttpHandler(_message);
        }

    }
}


CustomRouteHandler.cs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
using System.Web;
using System.Web.Routing;

namespace CustomRouteHandler
{
    public class CustomRouteHandler: IRouteHandler
    {
        private string _message;
        public CustomRouteHandler(string message)
        {
            _message = message;
        }

        public IHttpHandler GetHttpHandler(RequestContext requestContext)
        {
            return new CustomHttpHandler(_message);
        }

    }
}


Global.asax.cs


Custom Controller Factories

The routing system forwards a request to a controller factory. The controller factory is responsible for searching and creating controllers. MVC has a default controller factory called DefaultControllerFactory which is sufficient in most cases. However there are situations, such as when using dependency injection, that a custom controller factory is required. The following code is a shell for a custom controller factory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace develoq.mvc.ccfactory
{
  public class DeveloqControllerFactory : IControllerFactory
  {
    public IController CreateController(RequestContext requestContext, string controllerName)
    {
      //TODO :Insert IoC logic here
      return null;
    }
    public void ReleaseController(IController controller)
    {
      var disposable = controller as IDisposable;
      if (disposable != null)
      {
        disposable.Dispose();
      }
    }
  }
}


Custom Controller Factory

Attribute Routing

In MVC 5 and Web API 2, a new type of routing was introduced called Attribute Routing. The earlier form of routing called convention-based routing is still supported, but the new Attribute Routing provides more control over the URIs in a web application. To enable attribute routing, useroutes.MapMvcAttributeRoutes() for MVC or config.MapHttpAttributeRoutes() for Web API.

1
2
3
4
5
6
7
8
9
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute({resource}.axd/{*pathInfo});
 
        routes.MapMvcAttributeRoutes();
    }
}


routes.MapMvcAttributeRoutes()

You can make a URI parameter optional by adding a question mark to the route parameter. You can also specify a default value by using the form parameter=value. You can set a common prefix for an entire controller by using the [RoutePrefix] attribute. Use a tilde (~) on the method attribute to override the route prefix if needed. You can also apply the [Route] attribute on the controller level, capturing the action as a parameter. That route would then be applied on all actions in the controller, unless a specific [Route] has been defined on a specific action, overriding the default set on the controller.

Route constraints let you restrict how the parameters in the route template are matched. The general syntax is {parameter:constraint}. Below is a list of the predefined route constraints. You can create custom route constraints by implementing the IRouteConstraint interface.

Constraint Description Example
alpha Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {x:alpha}
bool Matches a Boolean value. {x:bool}
datetime Matches a DateTime value. {x:datetime}
decimal Matches a decimal value. {x:decimal}
double Matches a 64-bit floating-point value. {x:double}
float Matches a 32-bit floating-point value. {x:float}
guid Matches a GUID value. {x:guid}
int Matches a 32-bit integer value. {x:int}
length Matches a string with the specified length or within a specified range of lengths. {x:length(6)}
{x:length(1,20)}
long Matches a 64-bit integer value. {x:long}
max Matches an integer with a maximum value. {x:max(10)}
maxlength Matches a string with a maximum length. {x:maxlength(10)}
min Matches an integer with a minimum value. {x:min(10)}
minlength Matches a string with a minimum length. {x:minlength(10)}
range Matches an integer within a range of values. {x:range(10,50)}
regex Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)}


Defined Route Constraints



Error | ASP.NET Developer

Error

Error message

  • Warning: Cannot modify header information - headers already sent by (output started at /srv/disk9/1218369/www/kcshadow.net/aspnet/includes/common.inc:2748) in drupal_send_headers() (line 1232 of /srv/disk9/1218369/www/kcshadow.net/aspnet/includes/bootstrap.inc).
  • PDOException: SQLSTATE[42000]: Syntax error or access violation: 1142 INSERT command denied to user '1218369_b2cf'@'185.176.40.58' for table 'watchdog': INSERT INTO {watchdog} (uid, type, message, variables, severity, link, location, referer, hostname, timestamp) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9); Array ( [:db_insert_placeholder_0] => 0 [:db_insert_placeholder_1] => cron [:db_insert_placeholder_2] => Attempting to re-run cron while it is already running. [:db_insert_placeholder_3] => a:0:{} [:db_insert_placeholder_4] => 4 [:db_insert_placeholder_5] => [:db_insert_placeholder_6] => http://www.kcshadow.net/aspnet/?q=routing [:db_insert_placeholder_7] => [:db_insert_placeholder_8] => 54.162.171.242 [:db_insert_placeholder_9] => 1534727976 ) in dblog_watchdog() (line 160 of /srv/disk9/1218369/www/kcshadow.net/aspnet/modules/dblog/dblog.module).
The website encountered an unexpected error. Please try again later.