ASPを分析する.NET Core(Part 2)- AddMvc

14546 ワード

原文:https://www.stevejgordon.co.uk/asp-net-core-mvc-anatomy-addmvccore発表:2017年3月環境:ASP.NET Core 1.1
ASP.へようこそNET Coreソースシリーズ第2部.注意深い読者は、文章のタイトルが変わったことに気づき、「MVC」を外したのかもしれない.私が最も興味を持っているのはMVCの実現ですが、剖析が深まるにつれて、ASPに関わることは避けられません.NET Coreコアフレームワークの内容、例えばIrouter.したがって,適切な研究範囲の拡大が必要であり,「ASP.NET Coreの剖析」は本シリーズにとってより適切である.
Part 1ではAddMvcCore拡張法を理解し,本稿ではAddMvc拡張法を解析する.リリース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で説明したように、AddMvcCoreで生成されたIServiceCollectionおよびApplicationPartManagerへのアクセスを提供します.
AddMvcはAddApiExplorer拡張メソッドを呼び出し、builderのIServiceCollectionに2つのサービス登録を追加します.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();
    services.TryAddEnumerable(
        ServiceDescriptor.Transient());
}

次に、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());
}

まずMicrosoftを呼び出しましたAspNetCore.AuthorizationプログラムセットのAddAuthorization拡張方法.ここでは詳しく説明しませんが、認証(authorization)を有効にするコアサービスが追加されています.その後、サービスコールにAuthorizationApplicationModelProviderが追加されます.
次に、静的プライベートメソッドAddDefaultFrameworkPartsを呼び出してTagHelpersとRazor AssemblyPartsを追加するAddMvcマスターメソッドに戻ります.
private static void AddDefaultFrameworkParts(ApplicationPartManager partManager)
{
    var mvcTagHelpersAssembly = typeof(InputTagHelper).GetTypeInfo().Assembly;
    if(!partManager.ApplicationParts.OfType().Any(p => p.Assembly == mvcTagHelpersAssembly))
    {
        partManager.ApplicationParts.Add(new AssemblyPart(mvcTagHelpersAssembly));
    }

    var mvcRazorAssembly = typeof(UrlResolutionTagHelper).GetTypeInfo().Assembly;
    if(!partManager.ApplicationParts.OfType().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クラスを登録します.
次にAddViewを呼び出します.まずAddDataAnnotations拡張メソッドを使用してDataAnnotationsサービスを追加します.次に、ApplicationPartManagerにView ComponentFeatureProviderを追加します.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().Any())
    {
        manager.FeatureProviders.Add(new ViewComponentFeatureProvider());
    }
}

次に、AddMvcはAddRazorView拡張メソッドを呼び出します.
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().Any())
    {
        builder.PartManager.FeatureProviders.Add(new TagHelperFeatureProvider());
    }

    if (!builder.PartManager.FeatureProviders.OfType().Any())
    {
        builder.PartManager.FeatureProviders.Add(new MetadataReferenceFeatureProvider());
    }

    if (!builder.PartManager.FeatureProviders.OfType().Any())
    {
        builder.PartManager.FeatureProviders.Add(new ViewsFeatureProvider());
    }
}

ここでAddViewを再び呼び出すのは私の好奇心を引き起こした.AddRazorView Engineを呼び出す前にAddViewを呼び出すのは余計なようです.AddRazorViewはすべてのサービスを登録してくれるからです.このような操作は安全であるが,これらの拡張手法はTryAdd...形式を用いているため,サービスの重複登録を回避できるが,このような重複呼び出しを見るとやはりおかしい.私の好奇心を満たすために、私はMvc GitHubで問題を開き、間違いなのか意図的に設計されているのかを証明しました.私はマイクロソフトのRyan Nowakから非常に迅速な返事をもらい、これが設計決定であることを確認し、依存関係をより明確にし、容易に見ることができます.
AddRazorView Engineは3つのfeature providersをApplicationPartManagerに追加しました.FeatureProviders:TagHelperFeatureProvider,M e t a d ataReferenceFeatureProvider,ViewsFeatureProvider.これらはrazorビュー機能を実現するために必要なサービスです.
AdddMvcは次に、Cache Tag Helper機能にサービスを追加するために別の拡張方法を呼び出す.これは非常に簡単な拡張方法で、必要なサービスが5つしか登録されていません.次にAddMvcに戻り、AddDataAnnotationsを再度呼び出します.AddViewでは、前述した設計決定と同様に、このメソッドが以前に呼び出されています.
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はいったい何をしたのか.