首页 > 代码库 > asp.net core mvc剖析:路由

asp.net core mvc剖析:路由

在mvc框架中,任何一个动作请求都会被映射到具体控制器中的方法上,那框架是如何完成这样一个过程的,现在我们就来简单分析下流程。

我们紧跟上面的主题,任何一个请求都会交给处理管道进行处理,那mvc处理的流程自然也应该处于这个管道中,在startup.cs文件的Configure方法中,我们会看到这样的代码

1
2
3
4
5
6
7
app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}",
                    defaults: new { area = "admin" });
            });

  这部分代码的作用我们都清楚,就是配置路由规则,把用户的请求,路由到控制器方法上,我们来看它里面怎么做到的。首先看下UseMvc方法,直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static IApplicationBuilder UseMvc(
            this IApplicationBuilder app,
            Action<IRouteBuilder> configureRoutes)
        {
            。。。。。。
            //实例化路由构造器
            var routes = new RouteBuilder(app)
            {
                //设置默认处理器,就是路由符合条件时使用MvcRouteHandler来处理请求
                DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
            };
            //配置路由规则
            configureRoutes(routes);
            //这句很重要,上面配置的全局的路由规则,我们同样可以在控制器或者控制器方法上使用RouteAttribute配置路由规则,这些规则会优先采用
            routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(app.ApplicationServices));
            //routes.Build方法生成IRouter对象,一会我们在看具体细节,然后通过UseRouter注册一个RouterMiddleware中间件
            return app.UseRouter(routes.Build());
        }

  

  我们来看下Build方法代码:

1
2
3
4
5
6
7
8
9
10
11
12
public IRouter Build()
        {
            //创建一个路由规则集合
            var routeCollection = new RouteCollection();
            //把配置的路由规则加入到集合中,这个Routes就是上面configureRoutes(routes)配置的
            foreach (var route in Routes)
            {
                routeCollection.Add(route);
            }
             
            return routeCollection;
        }

  

  configureRoutes中,通过MapRoute方法注册规则,我们看一下:

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
26
27
28
public static IRouteBuilder MapRoute(
            this IRouteBuilder routeBuilder,
            string name,
            string template,
            object defaults,
            object constraints,
            object dataTokens)
        {
            if (routeBuilder.DefaultHandler == null)
            {
                throw new RouteCreationException(Resources.FormatDefaultHandler_MustBeSet(nameof(IRouteBuilder)));
            }
            var inlineConstraintResolver = routeBuilder
                .ServiceProvider
                .GetRequiredService<IInlineConstraintResolver>();
            //new了一个Route对象,把这个对象加入到routeBuilder.Routes集合中
            routeBuilder.Routes.Add(new Route(
                routeBuilder.DefaultHandler,
                name,
                template,
                new RouteValueDictionary(defaults),
                new RouteValueDictionary(constraints),
                new RouteValueDictionary(dataTokens),
                inlineConstraintResolver));
            return routeBuilder;
        }

  路由规则配置好了,当用户请求过来后,又是如何进行匹配的?上面我们提到了RouterMiddleware中间件,用户请求会通过这个中间件进行处理,这个中间件Invoke方法的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public async Task Invoke(HttpContext httpContext)
        {
            var context = new RouteContext(httpContext);
            //这句目前没有搞清楚作用是什么
            context.RouteData.Routers.Add(_router);
            //_router就是我们上面通过Build方法创建的,它就是RouteCollection
            await _router.RouteAsync(context);
            //判断是否找到了匹配的规则,这里的Handler只有当规则匹配了,才会赋值,Handler是什么?留一个疑问
            if (context.Handler == null)
            {
                _logger.RequestDidNotMatchRoutes();
                //如果没有任何路由符合要求,直接执行下一个中间件
                await _next.Invoke(httpContext);
            }
            else
            {
                httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature()
                {
                    RouteData = http://www.mamicode.com/context.RouteData,
                };
                //使用Handler处理请求
                await context.Handler(context.HttpContext);
            }
        }

  

  下面来看RouteCollection的RouteAsync方法实现:

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
26
27
28
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
                {
                    //调用Route对象的RouteAsync方法,匹配规则
                    await route.RouteAsync(context);
                    //规则匹配成功,直接break
                    if (context.Handler != null)
                    {
                        break;
                    }
                }
                finally
                {
                    if (context.Handler == null)
                    {
                        snapshot.Restore();
                    }
                }
            }
        }

  

  具体匹配方式不再介绍了,就是根据请求的路径跟设置的地址规则进行对比,我们只看匹配成功后,做了什么?

1
2
3
4
5
6
protected override Task OnRouteMatched(RouteContext context)
{
            context.RouteData.Routers.Add(_target);
       //_target是routeBuilder.DefaultHandler,这个是在上面创建routeBuilder时设置的,是一个MvcRouteHandler
       return _target.RouteAsync(context);
}

  

  在MvcRouteHandler里完成了context.Handler的设置,下面是这个Handler的具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
context.Handler = (c) =>
            {
                var routeData = http://www.mamicode.com/c.GetRouteData();
                var actionContext = new ActionContext(context.HttpContext, routeData, actionDescriptor);
                if (_actionContextAccessor != null)
                {
                    _actionContextAccessor.ActionContext = actionContext;
                }
                //根据请求的动作信息创建一个IActionInvoker对象
                var invoker = _actionInvokerFactory.CreateInvoker(actionContext);
                if (invoker == null)
                {
                    throw new InvalidOperationException(
                        Resources.FormatActionInvokerFactory_CouldNotCreateInvoker(
                            actionDescriptor.DisplayName));
                }
                //完成动作执行
                return invoker.InvokeAsync();
            };

  上面调用过程如下图:

技术分享

  后面再详细介绍mvc具体执行过程。

  

asp.net core mvc剖析:路由