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 请求调用委托链的激活调用请看这篇文章

//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的选择等。

 
分类: asp.net core

mvc route的注册,激活,调用流程的更多相关文章

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

    .net core mvc route的注册,激活,调用流程 mvc的入口是route,当前请求的url匹配到合适的route之后,mvc根据route所指定的controller和action激活c ...

  2. 2017.3.31 spring mvc教程(二)核心流程及配置详解

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

  3. 白话学习MVC(五)Controller的激活

    一.概述 在此系列开篇的时候介绍了MVC的生命周期 , 对于请求的处理,都是将相应的类的方法注册到HttpApplication事件中,通过事件的依次执行从而完成对请求的处理.对于MVC来说,请求是先 ...

  4. .net core + eureka + spring boot 服务注册与调用

    .net core + eureka + spring boot 服务注册与简单的调用 假期小长假遇上疫情只能去家里蹲了,刚好有时间总结一下. 概述 微服务架构是当前比较火的分布式架构,本篇基于.ne ...

  5. android从应用到驱动之—camera(1)---程序调用流程

    一.开篇 写博客还得写开篇介绍,可惜,这个不是我所擅长的.就按我自己的想法写吧. 话说camera模块,从上层到底层一共包含着这么几个部分: 1.apk------java语言 2.camera的ja ...

  6. android从应用到驱动之—camera(1)---程序调用流程[转]

    一.开篇 写博客还得写开篇介绍,可惜,这个不是我所擅长的.就按我自己的想法写吧. 话说camera模块,从上层到底层一共包含着这么几个部分: 1.apk------java语言 2.camera的ja ...

  7. 【转】 Android的NDK开发(1)————Android JNI简介与调用流程

    原文网址:http://blog.csdn.net/conowen/article/details/7521340 ****************************************** ...

  8. jsp-3 简单的servlet连接mysql数据库 使用mvc的登录注册

    简单的servlet连接mysql数据库 使用mvc的登录注册 commons-dbutils-1.6 mysql-connector-java-5.1.40-bin c3p0-0.9.5.2 mch ...

  9. Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程

    /*********************************************************************************** * * 字符设备驱动基本操作及 ...

随机推荐

  1. Android消息机制源码分析

    本篇主要介绍Android中的消息机制,即Looper.Handler是如何协同工作的: Looper:主要用来管理当前线程的消息队列,每个线程只能有一个Looper Handler:用来将消息(Me ...

  2. python 获取星期几

    In [17]: now.strftime(%a),now.strftime(%w) Out[17]: ('Mon', '1') Directive Meaning %a Weekday name. ...

  3. 一位资深开发的个人经历 【转自百度贴吧 java吧 原标题 4年java 3年产品 现在又开始做android了】

    楼主2007年从一家天津的三流大学毕业.毕业前报了一个职位培训,毕业后可以推荐工作.因为推荐的公司都是北京的,所以就来北京了. 找了一个月工作,没有找到要我的,就在出租屋里宅了起来,打着考研的旗号,又 ...

  4. vim添加Vundle插件

    1.git clone https://github.com/VundleVim/Vundle.vim.git ~/.vim/bundle/Vundle.vim 2.vim /etc/vimrc se ...

  5. sqlalchemy多表联合查询(join)

    使用outerjoin instances = db.session.query(Instance.name, Instance.sep_status, User.email).outerjoin( ...

  6. Thrift 跨服务开发框架

    Thrift概述 Thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP,Ruby, Erla ...

  7. 新手开发android容易出现的错误(不断更新中...)

    才开始开发android app,因为以前一直是java开发,学习也比较容易. 记录下自己开发过程中出现的一些小问题: 静态变量 在开发中,因为习惯性的问题,经常将一些常用数据(如用户信息等)进行st ...

  8. 烂泥:vcenter5.5无AD下的安装与配置

    本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb. 公司现在的虚拟化使用的基本上都是vsphere,目前大约有7台物理机,为了更好的管理虚 ...

  9. Makefile变量

    自定义变量 = 是最基本的赋值,会把整个makefile展开之后再决定是多少 x=foo y=$(x)bar #y是asdbar,不是foobar x=asd := 是覆盖之前的值,和=不同,和赋值的 ...

  10. JavaScript选项卡/页签/Tab的实现

    选项卡,也称页签,英文用Tab(Module-Tabs)表示.Tab将不同的内容重叠放在一个布局块内,重叠的内容区里每次只有其中一个是可见的. Tab可以在相同的空间里展示更多的信息,它把相似的主题分 ...