Add routing priority to Asp. Net MVC and WebApi, mvcwebapi

Source: Internet
Author: User

Add routing priority to Asp. Net MVC and WebApi, mvcwebapi
I. Why do I need a route priority?

We all know that we are in Asp. net MVC project or WebApi project, the registered route has no priority. when the project is large, or has multiple regions, or multiple Web projects, or is developed using a plug-in framework, our route registration is probably not written in a file, but distributed in many files of different projects. As a result, the priority of the route is highlighted.

For example: App_Start/RouteConfig. cs

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

Areas/Admin/AdminAreaRegistration. cs

context.MapRoute(    name: "Login",    
url: "login", defaults: new { area = "Admin", controller = "Account", action = "Login", id = UrlParameter.Optional }, namespaces: new string[] { "Wenku.Admin.Controllers" });

If you first register the above general default route and then register the login route, then in any case, the first qualified route will be matched first, that is, the registration of the second route is invalid.
The reason for this problem is the order of registration of the two routes, and Asp. net MVC and WebApi do not have the concept of priority. Therefore, we need to implement this idea on our own today and add a concept of priority when registering a route.

Ii. Solution

1. Analyze the ingress of Route registration. For example, we create a project named mvc4.0.

public class MvcApplication : System.Web.HttpApplication{    protected void Application_Start()    {        AreaRegistration.RegisterAllAreas();        WebApiConfig.Register(GlobalConfiguration.Configuration);        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);        RouteConfig.RegisterRoutes(RouteTable.Routes);    }}

There are two entry points for registering Mvc routes:
A. AreaRegistration. RegisterAllAreas (); register a region route
B. RouteConfig. RegisterRoutes (RouteTable. Routes); Register the project route

There is a WebApi route registration entry:
WebApiConfig. Register (GlobalConfiguration. Configuration); Register a WebApi route

2. Process Analysis of registered routes
AreaRegistrationContext
RouteCollection
HttpRouteCollection

When registering a route, these three classes are used to register and process the route.

3. Route Priority Scheme
A. Change the ingress of Route Registration
B. Customize the RoutePriority and HttpRoutePriority of a route. Both classes have the Priority attribute.
C. Customize a RegistrationContext to register the route. The registered object is the preceding custom route.
D. After all the routes are registered, they are added to the RouteCollection and HttpRouteCollection in priority order.

Iii. Implementation

1. Routing Definition

public class RoutePriority : Route{    public string Name { get; set; }    public int Priority { get; set; }    public RoutePriority(string url, IRouteHandler routeHandler)        : base(url,routeHandler)    {    }}public class HttpRoutePriority{    public string Name { get; set; }    public int Priority { get; set; }    public string RouteTemplate{get;set;}    public object Defaults{get;set;}    public object Constraints{get;set;}     public HttpMessageHandler Handler{get;set;}}

2. Define the router registration interface

public interface IRouteRegister{    void Register(RegistrationContext context);}

3. Define the routing registration context class

public class RegistrationContext{    #region mvc    public List<RoutePriority> Routes = new List<RoutePriority>();    public RoutePriority MapRoute(string name, string url,int priority=0)    {        return MapRoute(name, url, (object)null /* defaults */, priority);    }    public RoutePriority MapRoute(string name, string url, object defaults, int priority = 0)    {        return MapRoute(name, url, defaults, (object)null /* constraints */, priority);    }    public RoutePriority MapRoute(string name, string url, object defaults, object constraints, int priority = 0)    {        return MapRoute(name, url, defaults, constraints, null /* namespaces */, priority);    }    public RoutePriority MapRoute(string name, string url, string[] namespaces, int priority = 0)    {        return MapRoute(name, url, (object)null /* defaults */, namespaces, priority);    }    public RoutePriority MapRoute(string name, string url, object defaults, string[] namespaces,int priority=0)    {        return MapRoute(name, url, defaults, null /* constraints */, namespaces, priority);    }    public RoutePriority MapRoute(string name, string url, object defaults, object constraints, string[] namespaces, int priority = 0)    {        var route = MapPriorityRoute(name, url, defaults, constraints, namespaces, priority);        var areaName = GetAreaName(defaults);        route.DataTokens["area"] = areaName;        // disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up        // controllers belonging to other areas        bool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);        route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;        return route;    }    private static string GetAreaName(object defaults)    {        if (defaults != null)        {            var property = defaults.GetType().GetProperty("area");            if (property != null)                return (string)property.GetValue(defaults, null);        }        return null;    }    private RoutePriority MapPriorityRoute(string name, string url, object defaults, object constraints, string[] namespaces,int priority)    {        if (url == null)        {            throw new ArgumentNullException("url");        }        var route = new RoutePriority(url, new MvcRouteHandler())        {            Name = name,            Priority = priority,            Defaults = CreateRouteValueDictionary(defaults),            Constraints = CreateRouteValueDictionary(constraints),            DataTokens = new RouteValueDictionary()        };        if ((namespaces != null) && (namespaces.Length > 0))        {            route.DataTokens["Namespaces"] = namespaces;        }        Routes.Add(route);        return route;    }    private static RouteValueDictionary CreateRouteValueDictionary(object values)    {        var dictionary = values as IDictionary<string, object>;        if (dictionary != null)        {            return new RouteValueDictionary(dictionary);        }        return new RouteValueDictionary(values);    }    #endregion    #region http    public List<HttpRoutePriority> HttpRoutes = new List<HttpRoutePriority>();    public HttpRoutePriority MapHttpRoute(string name, string routeTemplate, int priority = 0)    {        return MapHttpRoute(name, routeTemplate, defaults: null, constraints: null, handler: null, priority: priority);    }    public HttpRoutePriority MapHttpRoute(string name, string routeTemplate, object defaults, int priority = 0)    {        return MapHttpRoute(name, routeTemplate, defaults, constraints: null, handler: null, priority: priority);    }    public HttpRoutePriority MapHttpRoute(string name, string routeTemplate, object defaults, object constraints, int priority = 0)    {        return MapHttpRoute(name, routeTemplate, defaults, constraints, handler: null, priority: priority);    }    public HttpRoutePriority MapHttpRoute(string name, string routeTemplate, object defaults, object constraints, HttpMessageHandler handler, int priority = 0)    {        var httpRoute = new HttpRoutePriority();        httpRoute.Name = name;        httpRoute.RouteTemplate = routeTemplate;        httpRoute.Defaults = defaults;        httpRoute.Constraints = constraints;        httpRoute.Handler = handler;        httpRoute.Priority = priority;        HttpRoutes.Add(httpRoute);        return httpRoute;    }    #endregion}

4. Add the route Registration Method to the Configuration class.

public static Configuration RegisterRoutePriority(this Configuration config){    var typesSoFar = new List<Type>();    var assemblies = GetReferencedAssemblies();    foreach (Assembly assembly in assemblies)    {        var types = assembly.GetTypes().Where(t => typeof(IRouteRegister).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface);        typesSoFar.AddRange(types);    }    var context = new RegistrationContext();    foreach (var type in typesSoFar)    {        var obj = (IRouteRegister)Activator.CreateInstance(type);        obj.Register(context);    }    foreach (var route in context.HttpRoutes.OrderByDescending(x => x.Priority))        GlobalConfiguration.Configuration.Routes.MapHttpRoute(route.Name, route.RouteTemplate, route.Defaults, route.Constraints, route.Handler);    foreach (var route in context.Routes.OrderByDescending(x => x.Priority))        RouteTable.Routes.Add(route.Name, route);    return config;}private static IEnumerable<Assembly> GetReferencedAssemblies(){    var assemblies = BuildManager.GetReferencedAssemblies();    foreach (Assembly assembly in assemblies)        yield return assembly;}

In this way, you only need to modify the original registration entry in the Global. asax. cs file

Public class MvcApplication: System. web. httpApplication {protected void Application_Start () {WebApiConfig. register (GlobalConfiguration. configuration); FilterConfig. registerGlobalFilters (GlobalFilters. filters); RouteConfig. registerRoutes (RouteTable. routes); Configuration. instance (). registerComponents (). registerRoutePriority (); // register a custom route }}

In each project, you only need to inherit the custom route registration interface IRouteRegister, for example:

Public class regiister: IRouteRegister {public void Register (RegistrationContext context) {// Register the backend management logon route context. mapRoute (name: "Admin_Login", url: "Admin/login", defaults: new {area = "Admin", controller = "Account", action = "Login ", id = UrlParameter. optional}, namespaces: new string [] {"Wenku. admin. controllers "}, priority: 11); // register the default route context on the backend Management page. mapRoute (name: "Admin_default", url: "Admin/{controller}/{action}/{id}", defaults: new {area = "Admin ", controller = "Home", action = "Index", id = UrlParameter. optional}, namespaces: new string [] {"Wenku. admin. controllers "}, priority: 10); // register the mobile phone to access the WebApi routing context. mapHttpRoute (name: "Mobile_Api", routeTemplate: "api/mobile/{controller}/{action}/{id}", defaults: new {area = "mobile ", action = RouteParameter. optional, id = RouteParameter. optional, namespaceName = new string [] {"Wenku. mobile. http "}}, constraints: new {action = new StartWithConstraint ()}, priority: 0 );}}

Iv. Summary

This is an Asp. net Mvc has a very small function expansion, and small projects may not need this function. However, sometimes the project's registered route does not take effect, you should think of it because of the routing order, in this case, the routing priority function may bring convenience to you. In short, share it with friends in need for reference.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.