Custom Routing in ASP.NET Core and MVC 6

 

        Introduction:

 

                 Routing in ASP.NET allows us to map incoming requests with http handler. It also help us to generate links. The routing support was added in ASP.NET MVC first. Later, ASP.NET Webforms, ASP.NET Web API and SignalR also included routing support. Routing has been unified in ASP.NET Core to one implementation. A good overview of routing in ASP.NET Core can be found here. In this article, I will talk a little about custom routing in ASP.NET Core.

 

        Description:

 

                    In ASP.NET MVC 5.x.x(or before) when you need to override the routing system then you will override RouteBase class or Route class. In ASP.NET Core, you need to override TemplateRoute or implement IRouter or INamedRouter. interace INamedRouter is IRouter plus Name property. Let's create a custom route which only allows requests with url starting with /home. ,

 

    public class MyTemplateRoute : TemplateRoute
    {
        public MyTemplateRoute(IRouter target, string routeTemplate, IInlineConstraintResolver inlineConstraintResolver)
                        : base(target,
                               routeTemplate,
                               inlineConstraintResolver: inlineConstraintResolver)
        {
        }

        public MyTemplateRoute(IRouter target,
                             string routeTemplate,
                             IDictionary<string, object> defaults,
                             IDictionary<string, object> constraints,
                             IInlineConstraintResolver inlineConstraintResolver)
            : base(target, routeTemplate, defaults, constraints, inlineConstraintResolver)
        {
        }

        public MyTemplateRoute(IRouter target,
                             string routeName,
                             string routeTemplate,
                             IDictionary<string, object> defaults,
                             IDictionary<string, object> constraints,
                             IInlineConstraintResolver inlineConstraintResolver)
            : base(target, routeName, routeTemplate, defaults, constraints, inlineConstraintResolver)
        { }

        public async override Task RouteAsync(RouteContext context)
        {
            var requestPath = context.HttpContext.Request.Path.Value ?? string.Empty;
            if (!requestPath.StartsWith("/home", StringComparison.OrdinalIgnoreCase))
            {
                return;
            }
            await base.RouteAsync(context);
        }
    }

 

                    First of all note that MyTemplateRoute class inherits from TemplateRoute class. Next, I have overridden the RouteAsync method where I simply check whether the url starts with /home or not. If not then return otherwise call the base RouteAsync. Dead simple. Isn't? You can directly use this class in the Startup class by adding routes.Routes.Add. But it will be look more nicer and consistent if we add an extension method,

 

    public static class RouteBuilderExtensions
    {
        public static IRouteBuilder MapRoute(this IRouteBuilder routeCollectionBuilder,
                                             string name,
                                             string template)
        {
            MapMyCustomRoute(routeCollectionBuilder, name, template, defaults: null);
            return routeCollectionBuilder;
        }

        public static IRouteBuilder MapMyCustomRoute(this IRouteBuilder routeCollectionBuilder,
                                             string name,
                                             string template,
                                             object defaults)
        {
            return MapMyCustomRoute(routeCollectionBuilder, name, template, defaults, constraints: null);
        }

        public static IRouteBuilder MapMyCustomRoute(this IRouteBuilder routeCollectionBuilder,
                                             string name,
                                             string template,
                                             object defaults,
                                             object constraints)
        {
            var inlineConstraintResolver = routeCollectionBuilder
                                                       .ServiceProvider
                                                       .GetService<IInlineConstraintResolver>();
            routeCollectionBuilder.Routes.Add(new MyTemplateRoute(routeCollectionBuilder.DefaultHandler,
                                                                name,
                                                                template,
                                                                ObjectToDictionary(defaults),
                                                                ObjectToDictionary(constraints),
                                                                inlineConstraintResolver));
            return routeCollectionBuilder;
        }



        private static IDictionary<string, object> ObjectToDictionary(object value)
        {
            var dictionary = value as IDictionary<string, object>;
            if (dictionary != null)
            {
                return dictionary;
            }

            return new RouteValueDictionary(value);
        }
    }

 

                    Finally, you can use this custom route inside the Startup.cs file, 

 

    
        routes.MapMyCustomRoute(
                    name: "default", 
                    template: "{controller}/{action}/{id?}",
                    defaults: new { controller = "Home", action = "Index" });

 

                   Now only the requests with /home urls accepted by the custom route. You can find another example of custom route (which implements the IRouter class) at github. You can find the routing repository of ASP.NEt Core at here

 

 

                   

        Summary:

                    Custom routing in ASP.NET Core has been changed little bit. There are new classes/interfaces. In this article, I showed you how to create a custom route in ASP.NET Core.