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

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

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

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

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方法代码:

public IRouter Build()
{
//创建一个路由规则集合
var routeCollection = new RouteCollection();
//把配置的路由规则加入到集合中,这个Routes就是上面configureRoutes(routes)配置的
foreach (var route in Routes)
{
routeCollection.Add(route);
} return routeCollection;
}

  

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

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方法的代码:

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方法实现:

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

  

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

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

  

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

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

  

asp.net core mvc剖析:路由的更多相关文章

  1. asp.net core mvc剖析:启动流程

    asp.net core mvc是微软开源的跨平台的mvc框架,首先它跟原有的MVC相比,最大的不同就是跨平台,然后又增加了一些非常实用的新功能,比如taghelper,viewcomponent,D ...

  2. ASP.NET Core MVC的路由参数中:exists后缀有什么作用,顺便谈谈路由匹配机制

    我们在ASP.NET Core MVC中如果要启用Area功能,那么会看到在Startup类的Configure方法中是这么定义Area的路由的: app.UseMvc(routes => { ...

  3. ASP.NET Core MVC 之路由(Routing)

     ASP.NET Core MVC 路由是建立在ASP.NET Core 路由的,一项强大的URL映射组件,它可以构建具有理解和搜索网址的应用程序.这使得我们可以自定义应用程序的URL命名形式,使得它 ...

  4. asp.net core mvc剖析:mvc执行过程(一)

    前面介绍了路由的过程,我们再来看下MvcRouteHandler的代码: public Task RouteAsync(RouteContext context) { ...... //根据路由信息查 ...

  5. asp.net core mvc剖析:mvc动作选择

    一个http请求过来后,首先经过路由规则的匹配,找到最符合条件的的IRouter,然后调用IRouter.RouteAsync来设置RouteContext.Handler,最后把请求交给RouteC ...

  6. asp.net core mvc剖析:动作执行

    紧跟上一篇文章.通过路由和动作匹配后,最终会得到跟当前请求最匹配的一个ActionDescriptor,然后通过IActionInvoker执行动作. 我们先来看一下IActionInvoker如何得 ...

  7. asp.net core mvc剖析:KestrelServer

    KestrelServer是基于Libuv开发的高性能web服务器,那我们现在就来看一下它是如何工作的.在上一篇文章中提到了Program的Main方法,在这个方法里Build了一个WebHost,我 ...

  8. asp.net core mvc剖析:处理管道构建

    在启动流程文章中提到,在WebHost类中,通过BuildApplication完成http请求处理管道的构建.在来看一下代码: ...... //这个调用的就是Startup.cs类中的Config ...

  9. ASP.NET Core MVC/WebAPi如何构建路由?

    前言 本节我们来讲讲ASP.NET Core中的路由,在讲路由之前我们首先回顾下之前所讲在ASP.NET Core中的模型绑定这其中有一个问题是我在项目当中遇见的,我们下面首先来看看这个问题. 回顾A ...

随机推荐

  1. Linux下网络流量实时监控工具大全

    在工作中发现,经常因为业务的原因,需要即时了解某台服务器网卡的流量,虽然公司也部署了cacti软件,但cacti是五分钟统计的,没有即时性,并且有时候打开监控页面不方便,个人喜欢随手在某台服务器上输入 ...

  2. leetcode--003 LRU cache

    aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAABHAAAACmCAIAAAA9PO+sAAAgAElEQVR4nO3du3HbytvH8X8zqoB12A ...

  3. Java解析JSON文件的方法(一)

    一.首先需要在Eclipse工程中导入相关的jar包,jar包参见链接:http://yunpan.alibaba-inc.com/share/link/NdA5b6IFK 二.提供一份待解析的jso ...

  4. Iphone安装铃声

    PP助手 应用列表中打开铃声多多文档. 5点击铃声下载,找到下载的铃声,按下图所示步骤导出至电脑. 6在PP助手界面内,找到"视频音乐"标签,然后进入视频音乐分类下的铃声分类,点击 ...

  5. UVa 10700 - Camel trading

    题目大意:给一个不含括号.只有+和*运算的表达式,数字的范围在1到20之间,算出计算结果的可能最大值和最小值. 贪心,如果加法优先级比乘法高,那么得出的结果为最大值.(a+b)*c = a*c + b ...

  6. python 模块添加

    python包含子目录中的模块方法比较简单,关键是能够在sys.path里面找到通向模块文件的路径.下面将具体介绍几种常用情况: (1)主程序与模块程序在同一目录下: 如下面程序结构:`-- src  ...

  7. Grunt构建工具能做哪些事?

    Grunt到底有什么作用?一般用来干嘛? 很多前端的工作,包括Less编译.javascript压缩.Css压缩等零零碎碎的工作, 都可以让Grunt来做. 实际上在项目开发中,一般是前端代码 与 后 ...

  8. 避免Node.js中回调地狱

    为了解决这个阻塞问题,JavaScript严重依赖于回调,这是在长时间运行的进程(IO,定时器等)完成后运行的函数,因此允许代码执行经过长时间运行的任务. downloadFile('example. ...

  9. Ajax Not Found,asp.net mvc 中

    x前台代码: <script type="text/javascript"> $(document).ready(function () { $("#btnS ...

  10. linux学习笔记----权限与命令之间的关系(极重要)

    一.权限与命令之间的关系 二.文件与目录的默认权限与隐藏权限 1)umask为默认的权限(通过umask -S)查看你的系统的默认权限. umask的分数是指该默认值需要减掉的权限,比如说umask= ...