zoukankan      html  css  js  c++  java
  • 剖析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扩展方法,执行相同的配置和服务注册,包括创建了一个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到底做了些什么。

  • 相关阅读:
    2016年11-29 mysql数据库
    2016年11月25日网页项目知识
    11月22日 网页项目遇到知识
    2016年11月15
    document操作
    2016年11月8日 函数
    2016年11月7日 数组练习
    2016年11月6日数组
    2016年11月4日运算符与语句
    2016年11月3日JS脚本简介数据类型: 1.整型:int 2.小数类型: float(单精度) double(双精度) decimal () 3.字符类型: chr 4.字符串类型:sting 5.日期时间:datetime 6.布尔型数据:bool 7.对象类型:object 8.二进制:binary 语言类型: 1.强类型语言:c++ c c# java 2.弱类型语
  • 原文地址:https://www.cnblogs.com/lookerblue/p/7357507.html
Copyright © 2011-2022 走看看