.net core 源码解析-mvc route的注册,激活,调用流程(三)

.net core mvc route的注册,激活,调用流程

mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活controller并调用action完成mvc的处理流程。下面我们看看服务器是如何调用route的。
core mvc startup基本代码。重点在AddMvc和UseMvc

public class Startup{    
   
   public
IConfigurationRoot Configuration { get; }    // This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services)    {services.AddMvc();}    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)    {app.UseStaticFiles();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});} }

AddMvc:把各种service加入IOC容器。比如格式化提供程序,action定位器,controllerFactory,controller激活器等等,一应服务全部在这里加入。
UseMvc:最重要的一行代码:builder.UseMiddleware (router); 看到这行代码就清楚的知道route 这个handler 在这里加入到请求委托链拉

public static IMvcBuilder AddMvc(this IServiceCollection services){    var builder = services.AddMvcCore();builder.AddJsonFormatters();builder.AddCors();    return new MvcBuilder(builder.Services, builder.PartManager);
}public static IMvcCoreBuilder AddMvcCore(this IServiceCollection services, Action<MvcOptions> setupAction){    var builder = services.AddMvcCore();services.Configure(setupAction);    return builder;
}internal static void AddMvcCoreServices(IServiceCollection services){services.TryAddSingleton<IActionSelector, ActionSelector>();services.TryAddSingleton<ActionConstraintCache>();services.TryAddSingleton<IActionSelectorDecisionTreeProvider, ActionSelectorDecisionTreeProvider>();    // This has a cache, so it needs to be a singletonservices.TryAddSingleton<IControllerFactory, DefaultControllerFactory>();    // Will be cached by the DefaultControllerFactoryservices.TryAddTransient<IControllerActivator, DefaultControllerActivator>();services.TryAddEnumerable(ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());    // Route Handlersservices.TryAddSingleton<MvcRouteHandler>(); // Only one per appservices.TryAddTransient<MvcAttributeRouteHandler>(); // Many per app}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());
}public static IApplicationBuilder UseRouter(this IApplicationBuilder builder, IRouter router){    return builder.UseMiddleware<RouterMiddleware>(router);
}

如此,mvc的入口route handler就加入了我们的请求委托链中。后续服务器接收到的请求就能交由route匹配,查找action,激活action处理。

router middleware的激活调用

middleware 请求调用委托链的激活调用请看这篇文章

//middleware加入_components请求处理委托链
public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware){_components.Add(middleware);  
 return this; }
 
 public static class UseMiddlewareExtensions{  
 
   private const string InvokeMethodName = "Invoke";
      private static readonly MethodInfo GetServiceInfo = typeof(UseMiddlewareExtensions).GetMethod(nameof(GetService), BindingFlags.NonPublic | BindingFlags.Static);    //注册middleware    

public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, Type middleware, params object[] args)
   
{      
       var applicationServices = app.ApplicationServices;        //将middleware 加入请求处理委托链return app.Use(next =>{        //解析方法和参数。查找类的Invoke方法作为入口方法。所以middleware只要是个class就行。只要有一个功公共的Invoke方法即可。var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);          
         var invokeMethods = methods.Where(m => string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)).ToArray();    
         var methodinfo = invokeMethods[0];    
         var parameters = methodinfo.GetParameters();  
         var ctorArgs = new object[args.Length + 1];ctorArgs[0] = next;Array.Copy(args, 0, ctorArgs, 1, args.Length);  
         //创建middleware的实例。并且通过构造函数注入相关的servicevar instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);            //如果方法只有一个参数,默认它就是httpcontext。if (parameters.Length == 1){                  
           return (RequestDelegate)methodinfo.CreateDelegate(typeof(RequestDelegate), instance);}          
             //多余一个参数的则构建一个func。并从ioc容器解析参数注入var factory = Compile<object>(methodinfo, parameters);            return context =>{              
              var serviceProvider = context.RequestServices ?? applicationServices;              
               return factory(instance, context, serviceProvider);};});}//代码中的创建实例注入service,创建有多个参数的invoke方法注入service具体代码就不贴上来了,占地方。//构造函数就是匹配最适合的构造函数,然后从IServiceProvider get实例,注入。//多个参数的invoke就更简单了。直接从IServiceProvider get实例注入。

上述源代码git地址,aspnet/HttpAbstractions项目

route handler middleware代码

public class RouterMiddleware{  
   private readonly ILogger _logger;  
   private readonly RequestDelegate _next;
   private readonly IRouter _router;    //创建middleware的实例。并且通过构造函数注入相关的servicepublic RouterMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IRouter router)    {_next = next;_router = router;_logger = loggerFactory.CreateLogger<RouterMiddleware>();}    //被调用的方法。从这里开始进入mvc route。public async Task Invoke(HttpContext httpContext)    {  
        //此处的 IRouter router对象。是我们在Startup中routes.MapRoute...配置的route集合对象:RouteCollection。当然也还有比如attributeroute等等好几种route。var context = new RouteContext(httpContext);context.RouteData.Routers.Add(_router);        await _router.RouteAsync(context);      
          if (context.Handler == null){        //没有匹配到route的情况_logger.RequestDidNotMatchRoutes();      
               await _next.Invoke(httpContext);}    
        else{httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature(){RouteData = context.RouteData,};            //匹配到路由处理await context.Handler(context.HttpContext);}} }
//Microsoft.AspNetCore.Routing.RouteCollection
public async virtual Task RouteAsync(RouteContext context){  
 // Perf: We want to avoid allocating a new RouteData for each route we need to process.// We can do this by snapshotting the state at the beginning and then restoring it// for each router we execute.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{          //循环所有routes规则,逐一匹配,匹配到一个自然就break。await route.RouteAsync(context);        
           if (context.Handler != null)            
               break;}      
        finally{            if (context.Handler == null)snapshot.Restore();}} }

UseMvc中有一行非常重要的代码。给RouteBuilder的DefaultHandler赋值一个handler。记住这行代码,我们继续往下看。

public static IApplicationBuilder UseMvc(this IApplicationBuilder app, Action<IRouteBuilder> configureRoutes){   
 var routes = new RouteBuilder(app){DefaultHandler = app.ApplicationServices.GetRequiredService<MvcRouteHandler>(),}; }
 //我们在Startup中routes.MapRoute的所有调用最终调用方法都是这个。new Route( routeBuilder.DefaultHandler,....)//全部都指定了_target为routeBuilder.DefaultHandler

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>();routeBuilder.Routes.Add(new Route(routeBuilder.DefaultHandler,name, template, new RouteValueDictionary(defaults), new RouteValueDictionary(constraints), new RouteValueDictionary(dataTokens), inlineConstraintResolver));  
          return routeBuilder; }

到这里,我们的逻辑有点绕了,让我们理理清楚:
1.请求进到RouterMiddleware.Invoke()方法
2.调用RouteCollection.RouteAsync()方法,RouteCollection.RouteAsync方法中循环注册的每一个route对象。
并调用route对象的RouteAsync()方法(route对象的RouteAsync方法在它的父类中Microsoft.AspNetCore.Routing.RouteBase)。
这里说的route对象即时Startup中routes.MapRoute生成的route对象(Microsoft.AspNetCore.Routing.Route)。Route继承RouteBase,RouteBase实现IRouter接口
3.RouteBase.RouteAsync()判断当前请求是否符合当前route规则,如果匹配的话,则调用抽象方法OnRouteMatched
4.RouteBase的抽象方法OnRouteMatched,又回到Route对象的OnRouteMatched方法中。调用_target.RouteAsync();_target对象即上面代码中的routeBuilder.DefaultHandler。
5.来到Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler.RouteAsync()方法中。最重要的一行代码: context.Handler =....
6.调用堆栈最终返回到1中(RouterMiddleware.Invoke())。判断context.Handler == null。为null没找到route匹配的action。不为null则await context.Handler(context.HttpContext)
7.context.Handler即为5中赋值的func。即下面的代码,定位action,调用action。

//Microsoft.AspNetCore.Mvc.Internal.MvcRouteHandler.RouteAsync
public Task RouteAsync(RouteContext context){  
 var candidates = _actionSelector.SelectCandidates(context);  
  if (candidates == null || candidates.Count == 0){_logger.NoActionsMatched();      
    return TaskCache.CompletedTask;}    
    var actionDescriptor = _actionSelector.SelectBestCandidate(context, candidates);  
    if (actionDescriptor == null){_logger.NoActionsMatched();    
       return TaskCache.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.FormatActionInvokerFactory_CouldNotCreateInvoker(actionDescriptor.DisplayName));      
           return invoker.InvokeAsync();};    
       
           return TaskCache.CompletedTask; }

至此,route的处理流程大约交代清楚了。包括route的注册,route的激活,route的选择等。

相关文章: 

  • .net core 源码解析-web app是如何启动并接收处理请求

  • .net core 源码解析-web app是如何启动并接收处理请求(二) kestrel的启动

原文地址:http://www.cnblogs.com/calvinK/p/6226219.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/327301.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高可用性的几个级别

转载自 高可用性的几个级别 大家常说高可用&#xff0c;High Availablility&#xff0c;但是一般说到这个词的时候&#xff0c;具体指的什么方案呢&#xff1f; 级别一&#xff1a;FT (Fault Tolerance) 双击热备 通过创建与主实例保持虚拟同步的虚拟机&#xff0c;使应用在服…

mysql - Docker Wordpress连接到本地主机上的数据库服务器

视频上面的 docker service create --name mysql -p 3306:3306 --env MYSQL_ROOT_PASSWORDroot \ --env MYSQL_DATABASEwordpress \ --network demo \ --mount typevolume,sourcemysql-data,destination/var/lib/mysql \ mysql:5.7 docker service create -…

CoreCRM 开发实录——开始之新项目的技术选择

2016年11月&#xff0c;接受了一个工作&#xff0c;是对“悟空CRM”进行一些修补。这是一个不错的 CRM&#xff0c;开源&#xff0c;并提供一个 SaaS 的服务。正好微软的 .NET Core 和 ASP.NET Core 也发布了。于是就有了这个想法&#xff1a;使用 ASP.NET Core 来开发一个 CRM…

80%的程序员都不了解的调试技巧

转载自 80%的程序员都不了解的调试技巧 程序员的工作内容&#xff0c;除了大部分时间写代码之外&#xff0c;因为有不少的时间是用在调试代码上。甚至说不是在调试代码&#xff0c;就是即将调试代码。 :) 今天我们来谈谈调试代码的一些技巧&#xff0c;在使用IDE提供的debu…

复制vmware overLay网络无法ping通 ping www.baidu.com可以

因为忘记关闭防火墙了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 要永久关闭&#xff01;&#xff01; 修改hostname https://blog.csdn.net/qq_27327261/article/details/109100219 关闭防火墙 https://blog.csdn.net/qq_27327261/article/details/1…

2016.NET Core相关内容回顾

每一年的脚步的确是快&#xff0c;转眼间马上就2017。.NET Core 2014年宣布开源以来&#xff0c;在2016年发布了第一个版本&#xff0c;2017年将发布第二个版本&#xff0c;在这新年之际&#xff0c;我们回顾2016年&#xff0c;新的一年&#xff0c;带着理想和抱负继续出发。 1…

微服务化的数据库设计与读写分离

转载自 微服务化的数据库设计与读写分离 数据库永远是应用最关键的一环&#xff0c;同时越到高并发阶段&#xff0c;数据库往往成为瓶颈&#xff0c;如果数据库表和索引不在一开始就进行良好的设计&#xff0c;则后期数据库横向扩展&#xff0c;分库分表都会遇到困难。 对于…

centos7 切换中文输入法 无需安装

*************** 当你发现自己的才华撑不起野心时&#xff0c;就请安静下来学习吧&#xff01;***************

Consul 服务注册与服务发现

1. 服务注册 对 Consul 进行服务注册之前&#xff0c;需要先部署一个服务站点&#xff0c;我们可以使用 ASP.NET Core 创建 Web 应用程序&#xff0c;并且部署到 Ubuntu 服务器上。 ASP.NET Core Hell World 应用程序示例代码&#xff0c;只需要三个文件&#xff0c;Startup.cs…

tar (child): .tgz\r:无法 open: 没有那个文件或目录

Linux下运行bash脚本显示“: /usr/bin/env: "bash\r": 没有那个文件或目录 程序员小熊 2017-12-18 14:45:45 18395 收藏 7 分类专栏&#xff1a; Linux 版权 用 ./ 运行bash脚本文件出现 报错信息 /usr/bin/env: "bash\r": 没有那个文件或目录 错误原…

Entity Framework Core 1.1 升级通告

Entity Framework Core&#xff08;EF Core&#xff09;是一个轻量级的&#xff0c;可扩展和实体框架的跨平台版本。今天&#xff0c;我们宣布 Entity Framewor Core 1.1 正式可用了。 EF Core 和 .NET Core 遵循相同的发行周期。每2个月不断的改进和每6个月的新功能发布。这是…

聊聊微服务架构及分布式事务解决方案

转载自 聊聊微服务架构及分布式事务解决方案 分布式事务场景如何设计系统架构及解决数据一致性问题&#xff0c;个人理解最终方案把握以下原则就可以了&#xff0c;那就是&#xff1a;大事务小事务&#xff08;原子事务&#xff09;异步&#xff08;消息通知&#xff09;&am…

使用熔断器设计模式保护软件

作为软件开发人员&#xff0c;我们的生活是快节奏的&#xff0c;我们采用的是敏捷软件开发方法&#xff0c;迭代式的开发我们软件功能&#xff0c;开发完成提交测试&#xff0c;通过了QA的测试后被部署到生产环境&#xff0c;然后可怕的事情在生产环境里发生了&#xff0c;生产…

hql投影查询之—— [Ljava.lang.Object; cannot be cast to cn.bdqn.guanMingSys.entity.Notice

最近在做项目中遇到一个简单的问题&#xff0c;但是解决了好久&#xff0c;问题就是投影查询。 先来看看出现的异常&#xff1a; java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to cn.bdqn.guanMingSys.entity.Notice at cn.bdqn.guanMingSys.dao.i…

架构师之路:从码农到架构师你差了哪些

转载自 架构师之路&#xff1a;从码农到架构师你差了哪些 Web应用&#xff0c;最常见的研发语言是Java和PHP。 后端服务&#xff0c;最常见的研发语言是Java和C/C。 大数据&#xff0c;最常见的研发语言是Java和Python。 可以说&#xff0c;Java是现阶段中国互联网公司中&…

Entity Framework Core 实现MySQL 的TimeStamp/RowVersion 并发控制

将通用的序列号生成器库 从SQL Server迁移到Mysql 遇到的一个问题&#xff0c;就是TimeStamp/RowVersion并发控制类型在非Microsoft SQL Server数据库中的实现。SQL Server timestamp 数据类型与时间和日期无关。SQL Server timestamp 是二进制数字&#xff0c;它表明数据库中数…

干货 | Tomcat 连接数与线程池详解

转载自 干货 | Tomcat 连接数与线程池详解 前言 在使用tomcat时&#xff0c;经常会遇到连接数、线程数之类的配置问题&#xff0c;要真正理解这些概念&#xff0c;必须先了解Tomcat的连接器&#xff08;Connector&#xff09;。 在前面的文章 详解Tomcat配置文件server.xml…

120项优化: 超级爬虫Hawk 2.0重磅发布!

沙漠君在历时半年&#xff0c;修改无数bug&#xff0c;更新一票新功能后&#xff0c;在今天隆重推出最新改进的超级爬虫Hawk 2.0! 啥&#xff1f;你不知道Hawk干吗用的&#xff1f; 这是采集数据的挖掘机&#xff0c;网络猎杀的重狙&#xff01;半年多以前&#xff0c;沙漠君写…

github创建一个新的tag

https://git-scm.com/book/en/v2/Git-Basics-Tagging

phone6s home键按不动了怎么办 苹果6s home键按不动解决方法

iphone6s home键按不动了怎么办 苹果6s home键按不动解决方法 作者&#xff1a;佚名 来源&#xff1a;绿茶软件园 2016-02-15 15:53:35 iphone6s home键按不动了怎么办&#xff1f;有iphone用户遇到home键按不动了的问题&#xff0c;下文小乐哥带来苹果6s home键按不动解决方法…