剖析ASP.NET Core(Part 2)- AddMvc(译)_.NET_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > .NET > 剖析ASP.NET Core(Part 2)- AddMvc(译)

剖析ASP.NET Core(Part 2)- AddMvc(译)

 2017/8/14 15:31:27  lookerblue  程序员俱乐部  我要评论(0)
  • 摘要:原文:https://www.stevejgordon.co.uk/asp-net-core-mvc-anatomy-addmvccore发布于:2017年3月环境:ASP.NETCore1.1欢迎阅读剖析ASP.NETCore源代码系列第二部分。细心的读者可能发现文章标题发生了变化,去掉了“MVC”。虽然我最感兴趣的是MVC的实现,但随着剖析的深入,不可避免的会涉及到ASP.NETCore核心框架的内容,比如IRouter。因此,适当扩大研究范围是必要的,“剖析ASP
  • 标签:.net ASP.NET MVC net

原文: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扩展方法,执行相同的配置和服务注册,包括创建了一个ApplicationPartManagerAddMvcCore的返回类型是一个MvcCoreBuilderAddMvc使用一个变量保存(builder)。正如我在Part 1中提到的,它(builder)提供了对AddMvcCore生成的IServiceCollectionApplicationPartManager的访问。

AddMvc调用AddApiExplorer扩展方法,向builderIServiceCollection添加两个服务注册:ApiDescriptionGroupCollectionProviderDefaultApiDescriptionProvider

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添加TagHelpersRazor 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到底做了些什么。

发表评论
用户名: 匿名