Interpreting ASP. NET 5 & MVC6 series (10): Controller and Action, interpreting ASP. NET
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 version of MVC6 framework, a Controller base class is still provided.Url
,RouteData
,HttpContext
,Request
,Response
In addition,IServiceProvider
TypeResovler
This is a dependency injection container used to obtain the specified type of instance objects in the current request scope.
It complies with the following rules:
Inherited fromMicrosoft.AspNet.Mvc.Controller
The class must be a Controller, whether there is a Controller suffix or not. Do not inheritMicrosoft.AspNet.Mvc.Controller
To use the custom XXXController of as the MVC Controller, you must referenceMicrosoft.AspNet.Mvc
Related assembly. If you do not want the Controller class that meets the preceding conditions to act as the Controller, you must addNonControllerAttribute
Features. Similarly, if you do not want to use a method in a Controller as an Action, you must addNonActionAttribute
Features.
Note the following features:
Features |
Description |
ActionNameAttribute |
Define the Action name (which can be different from the Action method name) |
AcceptVerbsAttribute |
Defines the names of supported Http methods. One or more methods are supported. |
ActivateAttribute |
The dependency injection tag can be placed on attributes or fields with the set permission. |
ResponseCacheAttribute |
Set Client Cache for a Controller or Action. |
RequireHttpsAttribute |
The limit must be Https requests. |
RemoteAttribute |
When the request is marked as an Ajax request, the server does not verify the form. |
NonControllerAttribute |
Mark that this class is not a Controller. |
NonActionAttribute |
Mark this method as not an Action. |
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.Mvc
In the namespaceIAssemblyProvider
The interface overwrites all the assemblies that may define the Controller. The default implementation of this interface isDefaultAssemblyProvider
Class. In this class, it is necessary to set the condition that the Controller that defines MVC must reference one or more of the following assemblies. The list is as follows:
Microsoft.AspNet.MvcMicrosoft.AspNet.Mvc.CoreMicrosoft.AspNet.Mvc.ModelBindingMicrosoft.AspNet.Mvc.RazorMicrosoft.AspNet.Mvc.Razor.HostMicrosoft.AspNet.Mvc.TagHelpersMicrosoft.AspNet.Mvc.XmlMicrosoft.AspNet.PageExecutionInstrumentation.Interfaces
That is to say, if you define a referenceMicrosoft.AspNet.Mvc
The POCO Controller is regarded 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 search mechanism of the Controller. The first is to inheritIAssemblyProvider
ImplementationCandidateAssemblies
Method (or overloadDefaultAssemblyProvider
) To define your own logic. The interface is defined as follows:
public interface IAssemblyProvider{ IEnumerable<Assembly> CandidateAssemblies { get; }}
Another method may be relatively simpler, that is, to useIServicesCollection
To define the assembly to be searched:
services.AddMvc().WithControllersAsServices(new[]{ typeof(MyController).Assembly, typeof(ExternalPocoController).Assembly});
After using the above Code, the system willDefaultAssemblyProvider
SwitchFixedSetAssemblyProvider
To implement the above judgment mechanism, that is, to search in a fixed range of sets.
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,Microsoft.Framework.Runtime
InILibraryManager
Interface instanceGetReferencingLibraries
Method, you can find how many assemblies reference one of the above lists. For exampleMicrosoft.AspNet.Mvc
To find the number of assemblies that reference the Assembly. The example is as follows:
var col = this.Resolver.GetRequiredService<ILibraryManager>();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<ILibraryInformation> GetCandidateLibraries(){ if (ReferenceAssemblies == null) { return Enumerable.Empty<ILibraryInformation>(); } // 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 versionIControllerTypeProvider
Interface, which providesControllerTypes
The read-only attribute is used to obtain all the defined controllers. The interface definition is as follows:
public interface IControllerTypeProvider{ IEnumerable<TypeInfo> ControllerTypes { get; }}
DefaultControllerTypeProvider
Is the default implementation of this interface. When querying qualified Controller, this default implementation class definesIsController
Method to Determine whether a type is Controller. The specific logic is as follows:
Protected internal virtual bool IsController ([NotNull] TypeInfo typeInfo, [NotNull] ISet <Assembly> 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) // This 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 it by yourselfIControllerTypeProvider
Interface to define its own Controller judgment logic, but it is fixed with some assembly types, MVC inIServicesCollection
An extension method is also provided to limit some specific Controller types. The example is as follows:
services.AddMvc().WithControllersAsServices(new[] { typeof(MyController), typeof(ExternalPocoController) });
After using the above Code, the system willDefaultControllerTypeProvider
SwitchFixedSetControllerTypeProvider
To implement the above judgment mechanism, that is, to limit some specific classes as Controller, other types cannot be as Controller.
Action lookup mechanism
The Action is selected throughIActionSelector
Interface default implementation classDefaultActionSelector
InSelectAsync
Select the most matched Action through context and route data. The Code is as follows:
public Task<ActionDescriptor> SelectAsync([NotNull] RouteContext context){ // ...}
There is another way to determine whether a method is Action, that isIActionModelBuilder
Interface. The default implementation of this interface isDefaultActionModelBuilder
Class. The implementation method is as follows:
Public IEnumerable <ActionModel> BuildActionModels ([NotNull] TypeInfo typeInfo, [NotNull] MethodInfo methodInfo) {if (! IsAction (typeInfo, methodInfo) {return Enumerable. Empty <ActionModel> () ;}// ...... omit other code}
This implementation method uses an internalIsAction
Method to Determine whether the method is a real Action method. The 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) // The NonActionAttribute attribute {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) // it cannot be a static method {return false;} if (methodInfo. isAbstract) // cannot be an abstract method {return false;} if (methodInfo. isConstructor) // cannot be the {return false;} if (methodInfo. isGenericMethod) // cannot be a generic method {return false;} return methodInfo. isPublic; // The Public method must be used}
The above content is important Code related to Controller and Action Search. For detailed principles and steps, seeMicrosoft.AspNet.Mvc.Core
All source code under the Assembly.