Interpretation of ASP. NET 5 & MVC6 series (10): Controller and Action
We know that in MVC5 and earlier versions, the lifecycles of the two frameworks are different. In the new version of MVC6, the MVC Controller/Web API Controller has already been integrated into one, this chapter describes the definition and use of Controller and Action, and how to query the corresponding Controller and Action based on the routing in the MVC framework.
Definition and use of Controller & Action
In the new MVC6 framework, a Controller base class is still provided. In addition to Url, RouteData, HttpContext, Request, and Response, A Resovler attribute of the IServiceProvider type is also provided, which is a dependency injection container used to obtain instance objects of the specified type in the current request scope.
It complies with the following rules:
The classes inherited from Microsoft. AspNet. Mvc. Controller must be controllers, regardless of the Controller suffix.
If you do not inherit the custom XXXController of Microsoft. AspNet. Mvc. Controller as the MVC Controller, you must reference the Microsoft. AspNet. Mvc-related assembly.
If you do not want the Controller class that meets the preceding conditions to act as the Controller, you must add the NonControllerAttribute feature to the class.
Similarly, if you do not want to use a method in a Controller as an Action, you must add the NonActionAttribute feature to the method.
Note the following features:
Search Mechanism of Controller
From the above chapter, we know that MVC6 not only supports the normal Controller (inherited from the sub-class of the Controller base class), but also supports the Controller of POCO, in this section, we will study the Controller's search principle and mechanism.
First, to determine whether a class is a Controller, you must first determine the number of datasets in which such classes are defined. Microsoft. aspNet. the IAssemblyProvider interface in the Mvc namespace overwrites all the assemblyprovider classes that may define the Controller. The default implementation of this interface is the DefaultAssemblyProvider class. In this class, the necessary conditions are, the Controller that defines MVC must reference one or more of the following sets. The list is as follows:
- 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
That is to say, if you define a DLL class library that references Microsoft. AspNet. Mvc, the POCO Controller in it will be considered as the Controller of MVC. In other words, if the POCO Controller class you define does not reference any assembly in the above assembly, these Controller classes will not be considered MVC Controller.
Assembly search
Currently, there are two ways to customize the Controller's search mechanism. The first is to inherit IAssemblyProvider to implement the CandidateAssemblies method (or overload DefaultAssemblyProvider) to define its own logic. The interface is defined as follows:
- public interface IAssemblyProvider
- {
- IEnumerable CandidateAssemblies { get; } }
Another method may be relatively simpler, that is, to use the extension method defined on IServicesCollection to define the assembly to be searched:
- services.AddMvc().WithControllersAsServices(new[]
- {
- typeof(MyController).Assembly,
- typeof(ExternalPocoController).Assembly
- });
After the above code is used, the system will switch DefaultAssemblyProvider to FixedSetAssemblyProvider to implement the above judgment mechanism, that is, searching in a set of fixed ranges.
Assembly screening
After the Assembly is determined, another problem arises. How can we determine whether an assembly references the Assembly listed in the necessary conditions of the above MVC? The answer is: In the GetReferencingLibraries method of the ILibraryManager interface instance in Microsoft. Framework. Runtime, you can find how many assemblies reference one of the above lists. For example, you can find the number of assemblies referenced by Microsoft. AspNet. Mvc according to the Microsoft. AspNet. Mvc assembly. The example is as follows:
- var col = this.Resolver.GetRequiredService(); var data = col.GetReferencingLibraries("Microsoft.AspNet.Mvc");
The usage code of this function in the default DefaultAssemblyProvider implementation class is as follows:
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 judgment
After determining the Assembly that meets the necessary conditions, you can traverse all the types in the Assembly and then determine whether the type is Controller. In the Controller judgment of the new version, this function is implemented by an IControllerTypeProvider interface, which provides the ControllerTypes read-only attribute to obtain all the defined controllers. The interface definition is as follows:
- public interface IControllerTypeProvider
- {
- IEnumerable ControllerTypes { get; } }
DefaultControllerTypeProvider is the default implementation of this interface. When querying qualified controllers, this default implementation class defines an IsController method to determine whether a type is Controller. The specific logic is as follows:
Protected internal virtual bool IsController ([NotNull] TypeInfo typeInfo, [NotNull] ISet candidateAssemblies) {if (! TypeInfo. isClass) // This type must be a class {return false;} if (typeInfo. isAbstract) // This class must not be an abstract class {return false;} // We only consider public top-level classes as controllers. isPublic returns false for nested // classes, regardless of visibility modifiers if (! TypeInfo. isPublic) // The class must be a Public class and not nested. The nested class cannot be Controller {return false;} if (typeInfo. containsGenericParameters) // This class cannot be a generic class {return false;} if (! TypeInfo. Name. EndsWith (ControllerTypeName, StringComparison. OrdinalIgnoreCase )&&! DerivesFromController (typeInfo, candidateAssemblies) // This class ends with a Controller, or inherits from the Controller base class, or its parent class is also a Controller. {Return false;} if (typeInfo. IsDefined (typeof (NonControllerAttribute) // This class cannot set the NonControllerAttribute attribute {return false;} return true ;}
You can also implement the IControllerTypeProvider interface to define your Controller judgment logic. However, with some assembly types fixed, MVC also provides an extension method in IServicesCollection to limit some specific Controller types, example:
- services.AddMvc().WithControllersAsServices(new[]
- {
- typeof(MyController),
- typeof(ExternalPocoController)
- });
After using the above Code, the system will switch DefaultControllerTypeProvider to FixedSetControllerTypeProvider to implement the above judgment mechanism, that is, to restrict certain classes as controllers, and Other types cannot be Controller.
Action lookup mechanism
Action Selection is implemented through the default implementation class DefaultActionSelector of the IActionSelector interface. In the implementation of the SelectAsync method, select the most matched Action through context and routing data. The Code is as follows:
- public Task SelectAsync([NotNull] RouteContext context) { // ... }
You can also determine whether a method is an Action, that is, the IActionModelBuilder interface. The default implementation of this interface is the defaactionactionmodelbuilder class. The implementation method is as follows:
Public IEnumerable BuildActionModels ([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo) {if (! IsAction (typeInfo, methodInfo) {return Enumerable. Empty ();} //... omit other code}
This implementation method uses an internal IsAction method to determine whether the method is a real Action method. The specific code is as follows:
- 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) // it cannot be a special name, such as an overloaded operator or attribute accessor)
- {
- Return false;
- }
-
- If (methodInfo. IsDefined (typeof (NonActionAttribute) // NonActionAttribute attribute cannot be declared
- {
- Return false;
- }
-
- // Overriden methods from Object class, e.g. Equals (Object), GetHashCode (), etc., are not valid.
- If (methodInfo. GetBaseDefinition (). DeclaringType = typeof (object) // it cannot be an overloaded method, such as Equals and GetHashCode
- {
- Return false;
- }
-
- // Dispose method implemented from IDisposable is not valid
- If (IsIDisposableMethod (methodInfo, typeInfo) // it cannot be the Dispose method.
- {
- Return false;
- }
-
- If (methodInfo. IsStatic) // cannot be a static method
- {
- Return false;
- }
-
- If (methodInfo. IsAbstract) // cannot be an abstract Method
- {
- Return false;
- }
-
- If (methodInfo. IsConstructor) // it cannot be a constructor.
- {
- Return false;
- }
-
- If (methodInfo. IsGenericMethod) // it cannot be a generic Method
- {
- Return false;
- }
-
- Return
- MethodInfo. IsPublic; // The Public method must be used.
- }
The above content is about the important Code related to Controller and Action Search. For detailed principles and steps, see all the source code under the Microsoft. AspNet. Mvc. Core Assembly.