ASPを解読する.NET 5&MVC 6シリーズチュートリアル(10):ControllerとAction

9246 ワード

MVC 5と以前のバージョンでは、2つのフレームワークのライフサイクルが異なることを知っています.新版MVC 6では、MVC Controller/Web API Controllerが統合されています.本章では、ControllerとActionの定義と使用、およびMVCフレームワークで、ルーティングに基づいて対応するControllerとActionをクエリーする方法について説明します.
Controller&Actionの定義と使用
新版MVC 6フレームワークでは、依然として1つのControllerベースクラスが提供されている.ここでは、依然としてUrlRouteDataHttpContextRequestResponse・の他に、現在のリクエストドメイン内で指定されたタイプのインスタンスオブジェクトを取得するために、注入に依存するコンテナに属するIServiceProviderタイプのResovler属性が提供されている.
以下のルールを遵守します.Microsoft.AspNet.Mvc.Controllerに継承されているクラスは、Controller接尾辞の有無にかかわらずコントローラであることは間違いありません.引き継がないMicrosoft.AspNet.Mvc.ControllerのカスタムXXXXControllerをMVC Controllerとするには、Microsoft.AspNet.Mvc関連するプログラムセットを参照する必要があります.上記の条件を満たすControllerクラスをControllerとしたくない場合は、そのクラスにNonControllerAttribute特性を加える必要があります.同様に,あるControllerのメソッドをActionとしたくない場合は,そのメソッドにNonActionAttribute特性を加える必要がある.
また、以下の特性に注意する必要があります.
とくせい
説明
ActionNameAttribute
アクションの名前を定義します(アクションメソッド名とは異なります).
AcceptVerbsAttribute
サポートされるHttp Methodの名前を定義し、単一または複数のMethodをサポートします.
ActivateAttribute
注入されたタグに依存して、set権限を持つ属性またはフィールドに置くことができます.
ResponseCacheAttribute
コントローラまたはアクションに対してクライアントキャッシュを設定します.
RequireHttpsAttribute
制限はHttpsリクエストでなければなりません.
RemoteAttribute
Ajaxリクエストとしてマークされ、サーバ側はformフォームの検証を検証しません.
NonControllerAttribute
クラスがControllerではないことをマークします.
NonActionAttribute
このメソッドをマークするのはアクションではありません.
コントローラの検索メカニズム
上記の章から,MVC 6は正常なController(Controllerベースクラスに継承されたサブクラス)だけでなく,POCOのControllerもサポートしていることが分かるが,本節ではControllerの検索原理メカニズムについて検討する.
まず,クラスがControllerであるかどうかを判断するには,まず,このようなクラスがどのくらいのプログラムセットで定義されているかを決定しなければならない.Microsoft.AspNet.Mvcネーミングスペース下のIAssemblyProviderインターフェースは、MVCを定義したコントローラをすべて上書きして検索するプログラムセットである.このインターフェースのデフォルト実装はDefaultAssemblyProviderクラスである.このクラスでは、MVCを定義したコントローラは、次のようなプログラムセットの1つ以上のプログラムセットを参照しなければならない必要がある.リストは以下の通りである.

Microsoft.AspNet.Mvc
Microsoft.AspNet.Mvc.Core
Microsoft.AspNet.Mvc.ModelBinding
Microsoft.AspNet.Mvc.Razor
Microsoft.AspNet.Mvc.Razor.Host
Microsoft.AspNet.Mvc.TagHelpers
Microsoft.AspNet.Mvc.Xml
Microsoft.AspNet.PageExecutionInstrumentation.Interfaces

つまり、Microsoft.AspNet.Mvcを引用したDLLクラスライブラリを定義すると、その中のPOCO ControlはMVCのControlとみなされます.言い換えれば、定義したPOCO Controlクラスが上記のプログラムセットのいずれかのプログラムセットを参照していない場合、これらのControlクラスはMVCのControlとはみなされません.
プログラムセットの検索
現在、Controllerの検索メカニズムをカスタマイズできる方法は2つあります.1つ目は、継承IAssemblyProvider実装CandidateAssembliesメソッド(またはリロードDefaultAssemblyProvider)で、独自のロジックを定義します.インタフェースの定義は次のとおりです.

public interface IAssemblyProvider
{
 IEnumerable CandidateAssemblies { get; }
}

もう1つの方法は、比較的簡単かもしれません.それは、IServicesCollectionで定義された拡張メソッドを使用して、検索するプログラムセットを定義することです.

services.AddMvc().WithControllersAsServices(new[]
{
 typeof(MyController).Assembly,
 typeof(ExternalPocoController).Assembly
});

上記コードを使用すると、システムはDefaultAssemblyProviderFixedSetAssemblyProviderに切り替え、一定範囲内のプログラムセットで検索するという判断メカニズムを実現する.
プログラムセットのフィルタ
プログラムセットが確定すると、もう一つの問題が来ますが、一つのプログラムセットが上記MVCの必要条件に記載されているプログラムセットを引用しているかどうかをどのように判断しますか?答えは、Microsoft.Framework.RuntimeILibraryManagerインタフェースインスタンスのGetReferencingLibrariesメソッドで、上記リストのいずれかのプログラムセットを参照しているプログラムがどれだけあるかを検索できます.たとえば、Microsoft.AspNet.Mvcプログラムセットに基づいて、このプログラムセットを参照しているプログラムセットの数を検索できます.例は次のとおりです.

var col = this.Resolver.GetRequiredService();
var data = col.GetReferencingLibraries("Microsoft.AspNet.Mvc");

この機能は、DefaultAssemblyProviderのデフォルト実装クラスで次のように使用されます.

protected virtual IEnumerable GetCandidateLibraries()
{
 if (ReferenceAssemblies == null)
 {
  return Enumerable.Empty();
 }

 // GetReferencingLibraries returns the transitive closure of referencing assemblies
 // for a given assembly.
 return ReferenceAssemblies.SelectMany(_libraryManager.GetReferencingLibraries)
        .Distinct()
        .Where(IsCandidateLibrary);
}

コントローラの判断
必要条件を満たすプログラムセットが決定されると、そのプログラムセット内のすべてのタイプを巡回し、そのタイプがControllerであるか否かを判断することができる.新版のController判定では、この機能を実現したのはIControllerTypeProviderインタフェースであり、このインタフェースはControllerTypesすべての定義のControllerを取得するための読み取り専用属性を提供し、インタフェース定義は以下の通りである.

public interface IControllerTypeProvider
{
 IEnumerable ControllerTypes { get; }
}
DefaultControllerTypeProviderこのインタフェースのデフォルト実装であり、条件に合致するControllerを問い合わせる際に、このデフォルト実装クラスは、1つのタイプがControllerであるか否かを判断するための方法を定義するIsController方法であり、具体的なロジックは以下の通りである.

protected internal virtual bool IsController([NotNull] TypeInfo typeInfo,
            [NotNull] ISet candidateAssemblies)
{
 if (!typeInfo.IsClass) //          
 {
  return false;
 }
 if (typeInfo.IsAbstract) //          
 {
  return false;
 }
 // We only consider public top-level classes as controllers. IsPublic returns false for nested
 // classes, regardless of visibility modifiers
 if (!typeInfo.IsPublic) //        Public (     ),       Controller
 {
  return false;
 }
 if (typeInfo.ContainsGenericParameters) //         
 {
  return false;
 }
 if (!typeInfo.Name.EndsWith(ControllerTypeName, StringComparison.OrdinalIgnoreCase) &&
  !DerivesFromController(typeInfo, candidateAssemblies)) //    Controller  ,    Controller  ,      Controller。
 {
  return false;
 }
 if (typeInfo.IsDefined(typeof(NonControllerAttribute))) //       NonControllerAttribute  
 {
  return false;
 }

 return true;
}

自分で実現することもできますIControllerTypeProviderインタフェースは自分のController判断ロジックを定義しますが、一部のプログラムセットタイプを固定することと、MVCはIServicesCollectionいくつかのControllerの特定のタイプを制限するための拡張方法を提供しています.例は以下の通りです.

services.AddMvc().WithControllersAsServices(new[]
 {
  typeof(MyController),
  typeof(ExternalPocoController)
 });

上記コードを使用すると、システムはDefaultControllerTypeProviderFixedSetControllerTypeProviderに切り替え、特定のクラスをコントローラとして制限し、他のタイプをコントローラとして使用できないという判断メカニズムを実現する.
Actionの検索メカニズム
Actionの選択は、IActionSelectorインタフェースのデフォルト実装クラスDefaultActionSelectorによって実現され、実装SelectAsyncメソッドでは、コンテキストとルーティングデータによって最も一致するActionが選択され、概略コードは以下の通りである.

public Task SelectAsync([NotNull] RouteContext context)
{
 // ...
}

もう一つ、アクションかどうかを判断する方法があります.それはIActionModelBuilderインタフェースです.このインタフェースのデフォルトはDefaultActionModelBuilderクラスで、実現方法は以下の通りです.

public IEnumerable BuildActionModels([NotNull] TypeInfo typeInfo,
             [NotNull] MethodInfo methodInfo)
{
 if (!IsAction(typeInfo, methodInfo))
 {
  return Enumerable.Empty();
 }
 // ....      
}

この実装方法は、1つの内部のIsAction方法によって、その方法が真のAction方法であるか否かを判断し、具体的なコードは以下の通りである.

protected virtual bool IsAction([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo)
{
 // The SpecialName bit is set to flag members that are treated in a special way by some compilers
 // (such as property accessors and operator overloading methods).
 if (methodInfo.IsSpecialName) //        (             )
 {
  return false;
 }

 if (methodInfo.IsDefined(typeof(NonActionAttribute))) //     NonActionAttribute  
 {
  return false;
 }

 // Overriden methods from Object class, e.g. Equals(Object), GetHashCode(), etc., are not valid.
 if (methodInfo.GetBaseDefinition().DeclaringType == typeof(object)) //        ,  Equals GetHashCode
 {
  return false;
 }

 // Dispose method implemented from IDisposable is not valid
 if (IsIDisposableMethod(methodInfo, typeInfo)) //    Dispose  
 {
  return false;
 }

 if (methodInfo.IsStatic) //        
 {
  return false;
 }

 if (methodInfo.IsAbstract) //        
 {
  return false;
 }

 if (methodInfo.IsConstructor) //        
 {
  return false;
 }

 if (methodInfo.IsGenericMethod) //        
 {
  return false;
 }

 return
  methodInfo.IsPublic; //    Public  
}

以上、ControllerとAction検索に関する重要なコードについて詳しくは、Microsoft.AspNet.Mvc.Coreプログラムセットのすべてのソースコードを参照してください.