. NET Core Development Log--brief description of routing

Source: Internet
Author: User
Tags httpcontext

Developers who have had ASP. NET or other modern web framework development experience should not be unfamiliar with the name of the word. If you want to use a sentence to explain what a route is, you can say this: by parsing the URL, you specify the appropriate handler.

Recall how routes are used in Web Forms applications:

public static void RegisterRoutes(RouteCollection routes){    routes.MapPageRoute("",        "Category/{action}/{categoryName}",        "~/categoriespage.aspx");}

Then the MVC application:

public static void RegisterRoutes(RouteCollection routes){    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");    routes.MapRoute(        "Default",                                                      "{controller}/{action}/{id}",                                  new { controller = "Home", action = "Index", id = "" }      );}

and then to ASP. NET Core:

public void Configure(IApplicationBuilder app){    app.UseMvc(routes =>    {        routes.MapRoute(            name: "default",            template: "{controller=Home}/{action=Index}/{id?}");    });}

You can also use a simpler notation:

public void Configure(IApplicationBuilder app){    app.UseMvcWithDefaultRoute();}

From the source of the two methods to see the implementation is the same.

public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app){    if (app == null)    {        throw new ArgumentNullException(nameof(app));    }    return app.UseMvc(routes =>    {        routes.MapRoute(            name: "default",            template: "{controller=Home}/{action=Index}/{id?}");    });}

The key is the content of the internal Usemvc method:

public static IApplicationBuilder UseMvc(    this IApplicationBuilder app,    Action<IRouteBuilder> configureRoutes){    ...    var routes = new RouteBuilder(app)    {        DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),    };    configureRoutes(routes);    routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));    return app.UseRouter(routes.Build());}

In this process, a Routebuilder object is instantiated first, and its DefaultHandler property is assigned a value of Mvcroutehandler. The routes is then executed as a parameter. Maproute method.

The process of Maproute is to add a new route object to the Routes collection in Routebuilder.

public static IRouteBuilder MapRoute(    this IRouteBuilder routeBuilder,    string name,    string template,    object defaults,    object constraints,    object dataTokens){    ...    var inlineConstraintResolver = routeBuilder        .ServiceProvider        .GetRequiredService<IInlineConstraintResolver>();    routeBuilder.Routes.Add(new Route(        routeBuilder.DefaultHandler,        name,        template,        new RouteValueDictionary(defaults),        new RouteValueDictionary(constraints),        new RouteValueDictionary(dataTokens),        inlineConstraintResolver));    return routeBuilder;}

One of the route objects is still not enough, and a attributeroute is inserted in the program.

The routes is then executed. Build (), returns the RouteCollection collection. The collection implements the Irouter interface.

public IRouter Build(){    var routeCollection = new RouteCollection();    foreach (var route in Routes)    {        routeCollection.Add(route);    }    return routeCollection;}

Finally, the route with the completed configuration is used.

public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router){    ...    return builder.UseMiddleware<RouterMiddleware>(router);}

And then saw the familiar middleware. In its core approach, RouteCollection's routeasync processing is called first.

public async Task Invoke(HttpContext httpContext){    var context = new RouteContext(httpContext);    context.RouteData.Routers.Add(_router);    await _router.RouteAsync(context);    if (context.Handler == null)    {        _logger.RequestDidNotMatchRoutes();        await _next.Invoke(httpContext);    }    else    {        httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()        {            RouteData = context.RouteData,        };        await context.Handler(context.HttpContext);    }}

The Routeasync method of each route is executed in sequence.

public async virtual Task RouteAsync(RouteContext context){    ...    for (var i = 0; i < Count; i++)    {        var route = this[i];        context.RouteData.Routers.Add(route);        try        {            await route.RouteAsync(context);            if (context.Handler != null)            {                break;            }        }        ...    }}

In the previous logic, Attributeroute and route were added to the routecollection.
* The loop will determine if the handler is assigned, in order to avoid further matches if the route has been matched. From the execution order, it is easy to understand that Attributeroute is higher than the average route priority.

Execute the Routeasync method in Attributeroute first:

public Task RouteAsync(RouteContext context){    var router = GetTreeRouter();    return router.RouteAsync(context);}

The Routeasync method for Treerouter is called:

Public async Task Routeasync (Routecontext context) {foreach (var tree in _trees) {var tokenizer = new Pathto Kenizer (context.        HttpContext.Request.Path); var root = tree.        Root;        var treeenumerator = new Treeenumerator (root, Tokenizer);            ... while (Treeenumerator.movenext ()) {var node = treeenumerator.current; foreach (var item in node. Matches) {var entry = Item.                Entry; var matcher = Item.                Templatematcher; try {if (!matcher. Trymatch (context. HttpContext.Request.Path, context.                    Routedata.values)) {continue; } if (! Routeconstraintmatcher.match (entry. Constraints, context. Routedata.values, context. HttpContext, this, Routedirection.incomingrequest,                        _constraintlogger)) {continue; } _logger. Matchedroute (entry. RouteName, entry.                    Routetemplate.templatetext); Context. ROUTEDATA.ROUTERS.ADD (entry.                    Handler); Await entry.                    Handler.routeasync (context); if (context.                    Handler = null) {return;        }                }                ...            } }    }}

If all Attributeroute routes do not match, no further processing is done. Otherwise, the Routeasync method in handler will continue to execute. The handler here is Mvcattributeroutehandler.

Public Task Routeasync (Routecontext context) {... var actiondescriptor = _actionselector.selectbestcandidate (context    , Actions); if (Actiondescriptor = = null) {_logger. Noactionsmatched (context.        Routedata.values);    return task.completedtask; } foreach (Var kvp in actiondescriptor.routevalues) {if (!string. IsNullOrEmpty (KVP. Value)) {context. ROUTEDATA.VALUES[KVP. Key] = kvp.        Value; }} context.        Handler = (c) = = {var routedata = C.getroutedata (); var actioncontext = new Actioncontext (context.        HttpContext, Routedata, Actiondescriptor);        if (_actioncontextaccessor! = null) {_actioncontextaccessor.actioncontext = Actioncontext;        } var invoker = _actioninvokerfactory.createinvoker (Actioncontext); if (invoker = = null) {throw new InvalidOperationException (Resources.formatactioninvoker               Factory_couldnotcreateinvoker (     Actiondescriptor.displayname)); } return Invoker.    InvokeAsync ();    }; return task.completedtask;}

The internal processing of this method only assigns values to the handler property of the Routecontext. The actual operation is to be performed at this step of the Invoke method in Routermiddleware context.Handler(context.HttpContext) .

As for the Routeasync method in the route:

  public virtual Task Routeasync (Routecontext context) {...    Ensurematcher (); Ensureloggers (context.    HttpContext); var Requestpath = context.    HttpContext.Request.Path; if (!_matcher. Trymatch (Requestpath, context. Routedata.values) {//If we got back a null value set, which means the URI did not match return task.com    Pletedtask; }//Perf:avoid accessing dictionaries if you don't need to write to them, these dictionaries is all//created LA    Zily. if (Datatokens.count > 0) {mergevalues (context.    Routedata.datatokens, Datatokens); } if (! Routeconstraintmatcher.match (Constraints, context. Routedata.values, context. HttpContext, this, Routedirection.incomingrequest, _constraintlogger)) {return task.comple    Tedtask; } _logger.    Matchedroute (Name, Parsedtemplate.templatetext); return onroutematched (context);}  

The Routeasync method of target is called in onroutematched only when the route is matched.

protected override Task OnRouteMatched(RouteContext context){    context.RouteData.Routers.Add(_target);    return _target.RouteAsync(context);}

The target here is the Mvcroutehandler that was passed in when the Routebuilder was originally created.

Public Task Routeasync (Routecontext context) {... var candidates = _actionselector.selectcandidates (context); if (candidates = = NULL | | candidates. Count = = 0) {_logger. Noactionsmatched (context.        Routedata.values);    return task.completedtask;    } var actiondescriptor = _actionselector.selectbestcandidate (context, candidates); if (Actiondescriptor = = null) {_logger. Noactionsmatched (context.        Routedata.values);    return task.completedtask; } context.        Handler = (c) = = {var routedata = C.getroutedata (); var actioncontext = new Actioncontext (context.        HttpContext, Routedata, Actiondescriptor);        if (_actioncontextaccessor! = null) {_actioncontextaccessor.actioncontext = Actioncontext;        } var invoker = _actioninvokerfactory.createinvoker (Actioncontext); if (invoker = = null) {throw new InvalidOperationException (Resources.formatactioninvoker Factory_Couldnotcreateinvoker (Actiondescriptor.displayname)); } return Invoker.    InvokeAsync ();    }; return task.completedtask;}

The process is similar to Mvcattributeroutehandler, which is to execute the handler method in the Routermiddleware invoke.

A mind map can simply summarize the above process.

Or you can describe the whole process in three words.

    • Add route
    • Match address
    • Processing requests

. NET Core Development Log--brief description of routing

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.