剖析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 ...
随机推荐
- 18:django 日志系统
django使用python内建的logging模块去建造自己的系统日志的,如果你想详细了解这个模块的话,请自己去看python的说明文档,这里仅仅介绍django中的日志系统 日志配置包括四个部分: ...
- python_day2学习笔记
基本数据类型 1.数字 int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,即-2147483648-2147483647 在64位系统上,整数的位数为64位, ...
- linux命令(41):watch命令
watch是一个非常实用的命令,基本所有的Linux发行版都带有这个小工具,如同名字一样,watch可以帮你监测一个命令的运行结果,省得你一遍遍的手动运行.在Linux下,watch是周期性的执行下个 ...
- Sqrt(x)——二分法,防越界
Implement int sqrt(int x). Compute and return the square root of x. 转自:http://blog.csdn.net/doc_sgl/ ...
- python中使用pyqt做GUI小试牛刀
import sys from PyQt4 import QtGui , QtCore class LIN(QtGui.QMainWindow): def _init_(self): QtGui.QM ...
- 主机批量扫描工具fping,hping安装及使用
https://blog.csdn.net/weixin_39762926/article/details/79476196?utm_source=blogxgwz0 https://blog.csd ...
- bzoj 2434 AC自动机 + fail指针建树 + 树状数组
思路:我们先跟着它给定的字符串走把字典树建出来,求出fail指针,我们考虑两个字符串 A和B, 如果想要求B中有多少A的子串,转换一下就是有多少个B的前缀的后缀包含A,这个在AC自动机 的状态图中很容 ...
- 关于xargs cp中,如何确定拷贝的源和目的
来源: http://bbs.chinaunix.net/thread-1022095-1-1.html Seker: find . -name "*" |xargs cp ??? ...
- 配置文件报错:元素类型 "XXX" 必须后跟属性规范 ">" 或 "/>"
这是一个比较常见的配置错误,一般分两步. 有的情况的确是xml文件出现了语法问题,可以让IDE来帮我们检查xml文件是否真的出现了语法错误.把xml内容复制进IDE中,让IDE帮我们检查错误. 如上图 ...
- 线段树+二进制位拆分【CF242E】XOR on Segment
Description 给定一个长为\(n(n<=10^5)\)的数组 数组里的数不超过\(10^6\) 有两种操作: 1:求\(sum[l,r]\); 2:对\([l,r]\)中的所有数和\( ...