在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 = 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 = 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具体执行过程。