剖析ASP.NET Core(Part 2)- AddMvc(译)
原文:https://www.stevejgordon.co.uk/asp-net-core-mvc-anatomy-addmvccore
发布于:2017年3月
环境:ASP.NET Core 1.1
欢迎阅读剖析ASP.NET Core源代码系列第二部分。细心的读者可能发现文章标题发生了变化,去掉了“MVC”。虽然我最感兴趣的是MVC的实现,但随着剖析的深入,不可避免的会涉及到ASP.NET Core 核心框架的内容,比如 IRouter。因此,适当扩大研究范围是必要的,“剖析ASP.NET Core”对本系列来说更加贴切。
在Part 1,我们了解了AddMvcCore扩展方法,本文我们将分析AddMvc扩展方法。我们继续使用rel/1.1.2版本的ASP.NET Core MVC。未来代码可能会发生变化,请使用相同版本。我们的起点仍是MvcSandbox项目,Startup.cs中的ConfigureServices方法如下:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
AddMvc扩展方法的实现:
public static IMvcBuilder AddMvc(this IServiceCollection services)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
} var builder = services.AddMvcCore(); builder.AddApiExplorer();
builder.AddAuthorization(); AddDefaultFrameworkParts(builder.PartManager); // Order added affects options setup order // Default framework order
builder.AddFormatterMappings();
builder.AddViews();
builder.AddRazorViewEngine();
builder.AddCacheTagHelper(); // +1 order
builder.AddDataAnnotations(); // +1 order // +10 order
builder.AddJsonFormatters(); builder.AddCors(); return new MvcBuilder(builder.Services, builder.PartManager);
}
上面代码首先调用我们在Part 1中分析过的AddMvcCore扩展方法,执行相同的配置和服务注册,包括创建了一个ApplicationPartManager。AddMvcCore的返回类型是一个MvcCoreBuilder,AddMvc使用一个变量保存(builder)。正如我在Part 1中提到的,它(builder)提供了对AddMvcCore生成的IServiceCollection和ApplicationPartManager的访问。
AddMvc调用AddApiExplorer扩展方法,向builder的IServiceCollection添加两个服务注册:ApiDescriptionGroupCollectionProvider和DefaultApiDescriptionProvider。
public static IMvcCoreBuilder AddApiExplorer(this IMvcCoreBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
} AddApiExplorerServices(builder.Services);
return builder;
} // Internal for testing.
internal static void AddApiExplorerServices(IServiceCollection services)
{
services.TryAddSingleton<IApiDescriptionGroupCollectionProvider, ApiDescriptionGroupCollectionProvider>();
services.TryAddEnumerable(
ServiceDescriptor.Transient<IApiDescriptionProvider, DefaultApiDescriptionProvider>());
}
接下来调用AddAuthorization扩展方法:
public static IMvcCoreBuilder AddAuthorization(this IMvcCoreBuilder builder)
{
AddAuthorizationServices(builder.Services);
return builder;
} internal static void AddAuthorizationServices(IServiceCollection services)
{
services.AddAuthorization(); services.TryAddEnumerable(
ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>());
}
首先它调用了Microsoft.AspNetCore.Authorization程序集中的AddAuthorization扩展方法。这里不深入介绍,但它添加了启用授权(authorization)的核心服务。之后,AuthorizationApplicationModelProvider被添加到ServicesCollection中。
接下来返回AddMvc主方法,它调用静态私有方法AddDefaultFrameworkParts添加TagHelpers和Razor AssemblyParts。
private static void AddDefaultFrameworkParts(ApplicationPartManager partManager)
{
var mvcTagHelpersAssembly = typeof(InputTagHelper).GetTypeInfo().Assembly;
if(!partManager.ApplicationParts.OfType<AssemblyPart>().Any(p => p.Assembly == mvcTagHelpersAssembly))
{
partManager.ApplicationParts.Add(new AssemblyPart(mvcTagHelpersAssembly));
} var mvcRazorAssembly = typeof(UrlResolutionTagHelper).GetTypeInfo().Assembly;
if(!partManager.ApplicationParts.OfType<AssemblyPart>().Any(p => p.Assembly == mvcRazorAssembly))
{
partManager.ApplicationParts.Add(new AssemblyPart(mvcRazorAssembly));
}
}
本方法首先获得Microsoft.AspNetCore.Mvc.TagHelpers中的InputTagHelper类的封装(Assembly),然后检查ApplicationPartManager中的ApplicationParts列表是否包含匹配的程序集,如果不存在则添加。然后使用同样方法处理Razor,使用的是Microsoft.AspNetCore.Mvc.Razor中的UrlResolutionTagHelper,同样执行如果不存在则添加操作。
再次返回AddMvc主方法,更多的项目通过扩展方法添加到ServiceCollection中。AddMvc调用AddFormatterMappings注册FormatFilter类。
接下来调用AddViews。首先使用AddDataAnnotations扩展方法添加DataAnnotations服务。接下来把ViewComponentFeatureProvider添加到ApplicationPartManager.FeatureProviders。最后注册了一些基于视图的服务,如singletons。这个类非常大,这里我不贴出所有代码。关键部分如下:
public static IMvcCoreBuilder AddViews(this IMvcCoreBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
} builder.AddDataAnnotations();
AddViewComponentApplicationPartsProviders(builder.PartManager);
AddViewServices(builder.Services);
return builder;
} private static void AddViewComponentApplicationPartsProviders(ApplicationPartManager manager)
{
if (!manager.FeatureProviders.OfType<ViewComponentFeatureProvider>().Any())
{
manager.FeatureProviders.Add(new ViewComponentFeatureProvider());
}
}
接下来AddMvc调用AddRazorViewEngine扩展方法:
public static IMvcCoreBuilder AddRazorViewEngine(this IMvcCoreBuilder builder)
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
} builder.AddViews(); // AddMvc主方法中执行过 AddViews()
AddRazorViewEngineFeatureProviders(builder);
AddRazorViewEngineServices(builder.Services);
return builder;
} private static void AddRazorViewEngineFeatureProviders(IMvcCoreBuilder builder)
{
if (!builder.PartManager.FeatureProviders.OfType<TagHelperFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider());
} if (!builder.PartManager.FeatureProviders.OfType<MetadataReferenceFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
} if (!builder.PartManager.FeatureProviders.OfType<ViewsFeatureProvider>().Any())
{
builder.PartManager.FeatureProviders.Add(new ViewsFeatureProvider());
}
}
这里再次调用AddViews引起了我的好奇。在AddRazorViewEngine之前调用AddViews似乎有些多余,因为AddRazorViewEngine会为我们注册所有服务。这样操作虽然是安全的,因为这些扩展方法都使用 TryAdd… 形式,能够避免服务的重复注册,但看到这样的重复调用还是觉得有些奇怪。为满足我的好奇,我在Mvc GitHub上开启了一个问题,求证是一个错误还是有意设计。我得到了微软Ryan Nowak非常迅速的回复,确认这是一个设计决定,使依赖关系更加明确和容易看出。
AddRazorViewEngine 添加了三个feature providers到ApplicationPartManager.FeatureProviders:TagHelperFeatureProvider,MetadataReferenceFeatureProvider和ViewsFeatureProvider。这些是实现razor视图功能必要的服务。
AdddMvc然后调用另一个扩展方法来为Cache Tag Helper功能添加服务。这是一个非常简单的扩展方法,仅注册了5个所需的服务。然后返回AddMvc,再次调用AddDataAnnotations。AddViews此前已调用过此方法,这与前面提到的设计决定相同。
AddMvc然后调用AddJsonFormatters扩展方法,将几个项目添加到ServicesCollection。
最后被调用扩展方法是Microsoft.AspNetCore.Cors中的AddCors,添加Cors相关的服务。
随着服务注册的完成,AddMvc创建了一个新的MvcBuilder,将当前的ServicesCollection和ApplicationPartManager作为属性存储。就像我们在第一篇文章中看到的MvcBuilder一样,MvcBuilder被描述为“允许细粒度的配置MVC服务”(allowing fine grained configurations of MVC services)。
AddMvc执行完成后,services collection共有148个注册服务,比AddMvcCore方法多了86个服务。ApplicationPartManager中有3个ApplicationParts和5个FeatureProviders。
小结
以上就是我对AddMvc的分析。相比前面对ApplicationPartManager的铺垫、创建分析,显得不那么让人兴奋,我们主要是调用扩展方法扩展服务。如你所见,许多服务是针对使用Views的Web 应用程序。如果是单纯的API应用程序,你可能不需要这些服务。在我开发过的API项目中,就使用AddMvcCore,同时通过builder的扩展方法添加额外几个我们需要的服务。下面是我在实际运用中的示例代码:
services.AddMvcCore(options =>
{
options.UseHtmlEncodeModelBinding();
})
.AddJsonFormatters(options =>
{
options.ContractResolver = new CamelCasePropertyNamesContractResolver();
})
.AddApiExplorer()
.AddAuthorization(options =>
{
options.DefaultPolicy =
new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireAssertion(ctx => ClaimsHelper.IsAdminUser(ctx.User.Claims.ToList()))
.Build(); options.AddPolicy(SecurityPolicyNames.DoesNotRequireAdminUser,
policy => policy.RequireAuthenticatedUser());
});
下篇文章我将讲解Startup.cs中的Configure方法调用的UseMvc到底做了些什么。
剖析ASP.NET Core(Part 2)- AddMvc(译)的更多相关文章
- 剖析ASP.NET Core MVC(Part 1)- AddMvcCore(译)
原文:https://www.stevejgordon.co.uk/asp-net-core-mvc-anatomy-addmvccore发布于:2017年3月环境:ASP.NET Core 1.1 ...
- 剖析ASP.NET Core(Part 4)- 调用MVC中间件(译)
原文:https://www.stevejgordon.co.uk/invoking-mvc-middleware-asp-net-core-anatomy-part-4 发布于:2017年5月环境: ...
- 剖析ASP.NET Core(Part 3)- UseMvc(译)
原文:https://www.stevejgordon.co.uk/asp-net-core-anatomy-part-3-addmvc 发布于:2017年4月环境:ASP.NET Core 1.1 ...
- [译]ASP.NET Core 2.0 系列文章目录
基础篇 [译]ASP.NET Core 2.0 中间件 [译]ASP.NET Core 2.0 带初始参数的中间件 [译]ASP.NET Core 2.0 依赖注入 [译]ASP.NET Core 2 ...
- .Net Core 学习 (1) - ASP.NET Core 总览
什么是ASP.NET 1.0 开源 - GitHub 跨平台 - 支持Windows, Mac, Linux 从底层进行了优化 - 使用最小开销的模块化组件 - 给与了开发人员很大的灵活性 为什么要使 ...
- [ASP.NET Core 2.0 前方速报]Core 2.0.3 已经支持引用第三方程序集了
发现问题 在将 FineUIMvc(支持ASP.NET MVC 5.2.3)升级到 ASP.NET Core 2.0 的过程中,我们发现一个奇怪的现象: 通过项目引用 FineUICore 工程一切正 ...
- 《ASP.NET Core项目开发实战入门》带你走进ASP.NET Core开发
<ASP.NET Core项目开发实战入门>从基础到实际项目开发部署带你走进ASP.NET Core开发. ASP.NET Core项目开发实战入门是基于ASP.NET Core 3.1 ...
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件) Startup Class 1.Startup Constructor(构造函数) 2.Configure ...
- ASP.NET Core 运行原理剖析
1. ASP.NET Core 运行原理剖析 1.1. 概述 1.2. 文件配置 1.2.1. Starup文件配置 Configure ConfigureServices 1.2.2. appset ...
随机推荐
- Struts2学习笔记04 之 拦截器
一.创建拦截器组件 1. 创建一个类,实现Interceptor接口,并实现intercept方法 2.注册拦截器 3.引用拦截器 二.拦截器栈 预置拦截器: 默认引用拦截器 拦截器调用顺序: Fil ...
- 从一个R语言案例学线性回归
线性回归简介 如下图所示,如果把自变量(也叫independent variable)和因变量(也叫dependent variable)画在二维坐标上,则每条记录对应一个点.线性回规最常见的应用场景 ...
- 手机端调试console.log,直接引入一个js文件
http://files.cnblogs.com/files/lwwen/mConsole.js 这是我写的一个原生js文件 直接引入即可,可以把html上面的需要打印的东西打印出来 <!DOC ...
- 监测mysql错误日志,有错误自动邮件报警
监测mysql错误日志,有错误自动邮件报警 http://blog.csdn.net/yabingshi_tech/article/details/51443401 MySQL:监控慢日志.错误日志. ...
- 【转载】C++ typedef用法小结
http://www.cnblogs.com/charley_yang/archive/2010/12/15/1907384.html 第一.四个用途 用途一: 定义一种类型的别名,而不只是简单的宏替 ...
- Java实现POI读取Excel文件,兼容后缀名xls和xlsx
1.引入所需的jar包: maven管理项目的话直接添加以下坐标即可: <!-- https://mvnrepository.com/artifact/org.apache.poi/poi -- ...
- 转:Awesome Vulnerability Research
转:https://github.com/re-pronin/Awesome-Vulnerability-Research Awesome Vulnerability Research
- MediaPlayer滑动不准的问题
因为MediaPlayer在seekto是异步进行的,如果在滑动过程中暂停,会导致滑动不准确的情况,这时候就需要添加滑动完成的监听即setOnSeekCompleteListener
- SpringBoot整合SpringBatch实用简例
SpringBatch主要是一个轻量级的大数据量的并行处理(批处理)的框架. 作用和Hadoop很相似,不过Hadoop是基于重量级的分布式环境(处理巨量数据),而SpringBatch是基于轻量的应 ...
- 洛谷——P1916 小书童——蚂蚁大战
P1916 小书童——蚂蚁大战 题目背景 小A在你的帮助下,开始“刷题”,他在小书童里发现了一款叫“蚂蚁大战”(又称蛋糕保卫战)的游戏.(你懂得) 题目描述 游戏中会出现n只蚂蚁,分别有a1,a2…… ...