剖析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 ...
随机推荐
- FineReport——笔记
1填报分页:需要在填报预览下的链接后添加:&__cutpage__=v:
- .NET Core、.NET Standard、Xamarin和.NET Framework对比
近日,微软发布了.NET Core 2.0,但是开发人员中间仍然存在一些疑惑,就是.NET Core..NET Standard.Xamarin和.NET Framework有什么不同. .NET F ...
- ueditor在QQ浏览器或者IE浏览器中无法加载
因为IE浏览器有兼容问题,打开网址,浏览器不一定以最新的文档模式加载.按F12查看 在你网址的head标签中加入:<meta http-equiv="x-ua-compatible&q ...
- resteasy json
https://www.cnblogs.com/toSeeMyDream/p/5763725.html
- Django在根据models生成数据库表时报错
报错信息: E:\Python\s6day103>python manage.py makemigrations Traceback (most recent call last): File ...
- es6扩展运算符及rest运算符总结
扩展运算符(...) 1.如果一个函数的参数个数不确定,可以用其代替 eg:求若干个数的和 2.改数组的引用为复制一份内存 此刻数组a也发生了变化,因为数组b是a的一个引用 此刻相当于复制了一份a 3 ...
- SpringMVC中为什么要配置Listener和Servlet
一直以来,我们使用SpringMVC的时候习惯性都配置一个ContextLoaderListener,虽然曾经有过疑问,配置的这个监听器和Servlet究竟做了什么,但也没深究. 要说任何Web框架都 ...
- 2017 Hackatari Codeathon B. 2Trees(深搜)(想法)
B. 2Trees time limit per test 0.75 seconds memory limit per test 256 megabytes input standard input ...
- Flask实战第52天:cms添加轮播图前端代码逻辑完成
首页我们在模态框中的保存按钮加一个id,这样方便我们选取这个按钮 <button type="button" class="btn btn-primary" ...
- CSS中包含块原理解析
CSS包含块原理解析 确定CSS中的包含块也确定就是元素的父元素.关键是:看元素是如何定位的.确定包含块很重要,比如设置百分比.另外也可以进行样式的继承等等. 分两个情况: 相对定位和静态定位 静态定 ...