ASP.NET Core MVC source code learning: Routing Routing

Disclaimer: This article is a blogger original article, shall not be reproduced without the bloggers allowed. https://blog.csdn.net/u011966339/article/details/90199248

Foreword

MVC as a basic part of the route, so before learning MVC or other source to learn about routing system, ASP.NET Core routing system relative to the previous Mvc great change, it re-integration of the Web Api and MVC.

Source Routing Address: https://github.com/aspnet/Routing

Routing (Routing) Features

Routing is an important part of MVC, which is mainly responsible for the received Http requests mapped to a specific routing handler in MVC that is routed to a specific Action Controller's.

Routing way to start is when ASP.NET Core MVC application starts as a middleware to start the detailed information will be given an article in the next.

It is popular, the URL is extracted from the routing information request, then matches this information to be mapped to the specific processing procedure, the routing is a middleware framework built on URL.
There is also a routing function is generated in response to a URL, a link address that is generated can be redirected or link.

Routing middleware mainly includes the following sections:

  • URL matches
  • URL generation
  • IRouter Interface
  • Routing template
  • Template constraints

Getting Started

ASP.NET Core Routing is divided into two projects, namely  Microsoft.AspNetCore.Routing.Abstractions, Microsoft.AspNetCore.Routing. The former is to provide a routing function for each abstraction, which is a concrete implementation.

We are in the process of reading the source code, I suggest that first quick look at the structure of the project, and then identify the key class, and then reading the entry procedure.

Microsoft.AspNetCore.Routing.Abstractions

After roughly after reading the entire structure, I may have found a few key interface, understand the role of these interfaces can help us do more with less in the subsequent reading.

IRouter

In  Microsoft.AspNetCore.Routing.Abstractions there is a critical interface  IRouter:

public interface IRouter
{
    Task RouteAsync(RouteContext context);

    VirtualPathData GetVirtualPath(VirtualPathContext context);
}

The interface is primarily doing two things, the first routing process is carried out in accordance with the routing context, the second context retrieving element is the virtual path  VirtualPathData.

IRouteHandler

Another key is the interface  IRouteHandler can be seen mainly abstract model of routing processing program, and an interface defined by name.

public interface IRouteHandler
{
    RequestDelegate GetRequestHandler(HttpContext httpContext, RouteData routeData);
}

It returns  RequestDelegate a delegate, the delegate may we are more familiar, encapsulates the approach to Http requests, located Microsoft.AspNetCore.Http.Abstractions in the blog should be a better understanding of the students I've seen before.

This interface  GetRequestHandler method has two parameters, the first is the HttpContext, not to say, mainly to see what the second parameter  RouteData.

RouteDataEncapsulating the data in the current routing information, which comprises three main attributes, respectively  DataTokensRoutersValues.

DataTokens: Are some of the key attributes related incidental path match in the dictionary.

Routers: Is a  Ilist<IRouter> list of instructions RouteData may contain sub-routes.

Values: It contains the current routing path key.

Another  RouteValueDictionary, which is a collection class, is primarily used to store some of the data in the routing information, not directly use  IEnumerable<KeyValuePair<string, string>> this data structure is to be, it is an object constructor receives an Object is stored in its internal conversion is more complex, it tries to Object object into a collection that they can recognize.

IRoutingFeature

I glance out the interface named according to the use of this interface, remember me in before the blog in about Http pipeline flow was mentioned when a called  Toolbox  thing what, this  IRoutingFeature is one of the components. We look at its definition:


public interface IRoutingFeature
{
    RouteData RouteData { get; set; }
}

It turned out he just packed  RouteDatato the HttpContext ah.

IRouteConstraint

This interface I looked at the comments when reading the original specifications of inspection route is mainly done by this interface.

We all know we write a Route Url address expression when sometimes write: Route("/Product/{ProductId:long}") there is a in this expression  {ProductId:long} parameter constraints, its main function is to rely on this interface to complete.


/// Defines the contract that a class must implement in order to check whether a URL parameter
/// value is valid for a constraint.
public interface IRouteConstraint
{
    bool Match(
        HttpContext httpContext,
        IRouter route,
        string routeKey,
        RouteValueDictionary values,
        RouteDirection routeDirection);
}

Microsoft.AspNetCore.Routing

Microsoft.AspNetCore.Routing The main is  Abstractions a major realization, we read the code when you can start reading it from the entrance.

RoutingServiceCollectionExtensions Is an extension of an extension class ASP.NET Core DI and used for ConfigService In this method, Routing Foreign exposed a IRoutingBuilder interface is used to allow users to add their own routing rules, we look at:

public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, Action<IRouteBuilder> action)
{
    //...略
    
    // 构造一个RouterBuilder 提供给action委托宫配置
    var routeBuilder = new RouteBuilder(builder);
    action(routeBuilder);
    
    //调用下面的一个扩展方法,routeBuilder.Build() 见下文
    return builder.UseRouter(routeBuilder.Build());
}

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

routeBuilder.Build() Construction of a collection  RouteCollection, to save all the  IRouter processing program information, comprising a user-configured Router.

RouteCollection Itself also achieved  IRouter , so it also has the ability to route processing.

Routing is intermediate the inlet  RouterMiddleware of this class, the registered processing flow duct through which the middleware Http, ASP.NET Core MVC it will default entry as part of its configuration, of course you can also use Routing separate out.

Let's look at  Invoke methods which do, which is located in RouterMiddleware.cs the file.

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);
    }
}

First, the initialization-routing context (RouteContext) by httpContext, then add routing rules configured by the user to the route context RouteData Routers go.

Next  await _router.RouteAsync(context) , it is to use the  IRouter interface  RouteAsync method has.

We then trace  RouteAsync the function, look inside what has been done? We track to RouteCollection.cs the class:

We look RouteAsync process:


public async virtual Task RouteAsync(RouteContext context)
{
    var snapshot = context.RouteData.PushState(null, values: null, dataTokens: null);

    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;
            }
        }
        finally
        {
            if (context.Handler == null)
            {
                snapshot.Restore();
            }
        }
    }
}

I think this class, including function design is very clever, if I were you, I do not necessarily want to come out, so we are by looking at the source code can also learn a lot of new knowledge.

Why is it clever design? RouteCollection He inherited IRouter but there is no specific treatment for the routes, but on the specific routing loop processing program by re-routing the context of the distribution. We look at his process:

1, in order to improve performance, create a snapshot object RouteDataSnapshot, RouteDataSnapshot is a structure that stores the routing data in the Route.

2, Router RouteCollection in the current cycle, add to RouterContext in the Routers, and then handed over to the RouterContext Router to deal with.

3, when no handler to process the current route  snapshot.Restore() re-initialize the snapshot state.

Next step is to look at specific routes to be processed, and we from  RouteBase the beginning.

1, RouteBase constructor initializes  RouteTemplateNameDataTokensDefaults.
Defaults route is the default configuration parameters.

2, RouteAsync will be a series of checks, if there is no match to the URL will be returned directly corresponding routing.

3, using the route parameter matcher  RouteConstraintMatcher to match, if not matched, return the same directly.

4, if the match is successful, it will trigger  OnRouteMatched(RouteContext context)function, it is an abstract function, the specific implementation is located  Route.cs in.

Then, we'll continue to track  Route.cs the OnRouteMatch, with a look:

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

_target worth handler current route, then exactly which route handler it? We explore together.

We know that we have created a total route MapRoute, MapGet, MapPost, MapPut, MapDelete, MapVerb... etc. write this way, we talk about each corresponding to its routing handler is kind of how, here is an example:


app.UseRouter(routes =>{
    routes.DefaultHandler = new RouteHandler((httpContext) =>
    {
        var request = httpContext.Request;
        return httpContext.Response.WriteAsync($"");
    });
                    
    routes
    .MapGet("api/get/{id}", (request, response, routeData) => {})
    .MapMiddlewareRoute("api/middleware", (appBuilder) => 
                         appBuilder.Use((httpContext, next) => httpContext.Response.WriteAsync("Middleware!")
                      ))
    .MapRoute(
          name: "AllVerbs",
          template: "api/all/{name}/{lastName?}",
          defaults: new { lastName = "Doe" },
          constraints: new { lastName = new RegexRouteConstraint(new Regex("[a-zA-Z]{3}",RegexOptions.CultureInvariant, RegexMatchTimeout)) });
});

Explain Following the above example,

MapRoute: Using this approach, we must give DefaultHandler assignment handler, otherwise it will throw an exception, under normal circumstances we will use RouteHandler class.

MapVerb: MapPost, MapPut, etc., and it is similar, it will be treated as a program commissioned to provide RequestDelegate out, which means we actually deal with things in their own HttpContext is not processed through RouteHandler.

MapMiddlewareRoute: IApplicationBuilder need to pass a delegate, is actually after IApplicationBuilder Build a RequestDelegate, it will be a RouteHandler class inside the new, then MapRoute call.

All of these are pointing the finger of blame RouteHandler, we take a look at  RouteHandler it.


public class RouteHandler : IRouteHandler, IRouter
{
    // ...略

    public Task RouteAsync(RouteContext context)
    {
        context.Handler = _requestDelegate;
        return TaskCache.CompletedTask;
    }
}

Did not do anything, just come RequestDelegate incoming assigned to the handler RouteContext of.

Finally, the code execution to the  RouterMiddleware class  Invoke last line of the method  await context.Handler(context.HttpContext), the time to start calling Handler delegate, execute user code.

to sum up

We summarize the above process:
First of all incoming requests to the registered RouterMiddleware middleware, then it RouteAsync sequentially invoke methods on each route. When a request comes, IRouter instance has been set to select whether to process  RouteContext Handler a non-empty RequestDelegate on. If the Route has been set for the request handler, then the routing process will be aborted and start calling Hanlder handler is set to handle the request. If the current request tried all the routes are not found handler, then call next, will request the next middleware to the pipeline.

Guess you like

Origin blog.csdn.net/u011966339/article/details/90199248