.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 singleton
services.TryAddSingleton<IControllerFactory, DefaultControllerFactory>();
// Will be cached by the DefaultControllerFactory
services.TryAddTransient<IControllerActivator, DefaultControllerActivator>();
services.TryAddEnumerable(ServiceDescriptor.Transient<IControllerPropertyActivator, DefaultControllerPropertyActivator>());
// Route Handlers
services.TryAddSingleton<MvcRouteHandler>(); // Only one per app
services.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加入_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的实例。并且通过构造函数注入相关的service
var 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的实例。并且通过构造函数注入相关的service
public 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 源码解析-mvc route的注册,激活,调用流程(三)的更多相关文章
- Spring-cloud & Netflix 源码解析:Eureka 服务注册发现接口 ****
http://www.idouba.net/spring-cloud-source-eureka-client-api/?utm_source=tuicool&utm_medium=refer ...
- .net core 源码解析-web app是如何启动并接收处理请求
最近.net core 1.1也发布了,蹒跚学步的小孩又长高了一些,园子里大家也都非常积极的在学习,闲来无事,扒拔源码,涨涨见识. 先来见识一下web站点是如何启动的,如何接受请求,.net core ...
- 深入源码解析类Route
微软官网对这个类的说明是:提供用于定义路由及获取路由相关信息的属性和方法.这个说明已经很简要的说明了这个类的作用,下面我们就从源码的角度来看看这个类的内部是如何工作的. public class Ro ...
- JFinal 源码解析-MVC部分
首先从请求入口看起,应用初始化时加载web.xml的JFinalFilter,和configClass 从这段配置可以看出jfinal和spring mvc入口类似,通过一个实现Servlet Fil ...
- .net core 源码解析-web app是如何启动并接收处理请求(二) kestrel的启动
上篇讲到.net core web app是如何启动并接受请求的,下面接着探索kestrel server是如何完成此任务的. 1.kestrel server的入口KestrelServer.Sta ...
- springIOC源码解析之BeanDefinition的注册
ApplicationContext类结构 context是一个存储上下文结构的东西,里面会引用BeanFactory BeanFactory类结构 我们从这句代码开始分析,(本文spring采用的 ...
- 【Dubbo 源码解析】04_Dubbo 服务注册&暴露
Dubbo 服务注册&暴露 Dubbo 服务暴露过程是通过 com.alibaba.dubbo.config.spring.ServiceBean 来实现的.Spring 容器 refresh ...
- Redis源码解析:15Resis主从复制之从节点流程
Redis的主从复制功能,可以实现Redis实例的高可用,避免单个Redis 服务器的单点故障,并且可以实现负载均衡. 一:主从复制过程 Redis的复制功能分为同步(sync)和命令传播(comma ...
- yolo源码解析(3):视频检测流程
代码在自己电脑中!!!!不在服务器 根据前文所说yolo代码逻辑: ├── examples │ ├── darknet.c(主程序) │ │── xxx1.c │ └── xxx2.c │ ├── ...
随机推荐
- sql 分组取最新的数据sqlserver巧用row_number和partition by分组取top数据
SQL Server 2005后之后,引入了row_number()函数,row_number()函数的分组排序功能使这种操作变得非常简单 分组取TOP数据是T-SQL中的常用查询, 如学生信息管理系 ...
- malloc 与 free函数详解<转载>
malloc和free函数详解 本文介绍malloc和free函数的内容. 在C中,对内存的管理是相当重要.下面开始介绍这两个函数: 一.malloc()和free()的基本概念以及基本用法: 1 ...
- Java内部类final语义实现
本文描述在java内部类中,经常会引用外部类的变量信息.但是这些变量信息是如何传递给内部类的,在表面上并没有相应的线索.本文从字节码层描述在内部类中是如何实现这些语义的. 本地临时变量 基本类型 fi ...
- 严重: Exception sending context initialized event to listener instance of class
问题描述:Exception sending context initialized event to listener instance of class org.springframework.w ...
- C++ STL简述
前言 最近要找工作,免不得要有一番笔试,今年好像突然就都流行在线笔试了,真是搞的我一塌糊涂.有的公司呢,不支持Python,Java我也不会,C有些数据结构又有些复杂,所以是时候把STL再看一遍了-不 ...
- 【HTML5&CSS3进阶03】Jser与Csser如何一起愉快的翻新老组件
上次,我们形成了两种header的布局,一种flexbox,一种float,最后与身边做重构的同事交流下来,选择了float的布局. 事实上布局的选型不需要我关注,我的参与或者一些意见多数是自我提升, ...
- JavaScript简单分页,兼容IE6,~3KB
简介 兼容IE6+及现代浏览器的简单分页,支持同一页面多个分页. 使用 Browser <link rel="stylesheet" href="css/GB-pa ...
- Android Weekly Notes Issue #228
Android Weekly Issue #228 October 23rd, 2016 Android Weekly Issue #228 本期内容包括: Android 7.1的App Short ...
- iOS:GCD理解1(同步-异步、串行-并行)
1.并行-异步(ST1与ST2抢占资源) 1-1).获取 并行(全局)队列 ,DISPATCH_QUEUE_PRIORITY_DEFAULT 为默认优先级. dispatch_queue_t queu ...
- iOS开发:创建真机调试证书及描述文件
iOS开发:创建真机调试证书及描述文件 关于苹果iOS开发,笔者也是从小白过来的,经历过各种困难和坑,其中就有关于开发证书,生产证书,in_house证书,add_Hoc证书申请过程中的问题,以及上架 ...