Interpretation of ASP. NET 5 MVC6 Series Tutorials (10): Controller and Action

  • 2021-07-26 07:29:34
  • OfStack

We know that in MVC5 and previous versions, the life cycles of the two frameworks are different. In the new version of MVC6, MVC, Controller/Web, API and Controller have been combined into 1. In this chapter, we mainly explain the definition and use of Controller and Action, and how to query the corresponding Controller and Action according to routes in MVC framework.

Controller & Definition and Use of Action

In the new version of MVC6 framework, one Controller base class is still provided, except that it is still provided here Url , RouteData , HttpContext , Request , Response In addition, 1 is also provided IServiceProvider Type of Resovler Property that is the container for dependency injection and is used to get an instance object of the specified type within the scope of the current request.

It abides by the following rules:

Inherit from Microsoft.AspNet.Mvc.Controller The class of is definitely a controller, with or without the Controller suffix. Do not inherit Microsoft.AspNet.Mvc.Controller To use the custom XXXController of MVC Controller, you must refer to Microsoft.AspNet.Mvc The associated assembly. If you don't want the Controller class that meets the above conditions to be Controller, you need to add RouteData0 Characteristics. Similarly, if you don't want a method in an Controller to be an Action, you need to add NonActionAttribute Characteristics.

There are also several features to note:

特性 描述
ActionNameAttribute 定义Action的名称(可以和Action方法名不同)
AcceptVerbsAttribute 定义支持的Http Method名称,支持单个或多个Method。
ActivateAttribute 依赖注入的标记,可以放在具有set权限的属性或字段上。
ResponseCacheAttribute 针对某个Controller或Action设置客户端缓存。
RequireHttpsAttribute 限制必须是Https请求。
RemoteAttribute 标记为Ajax请求,服务器端不验证form表单的验证。
NonControllerAttribute 标记该类不是Controller。
NonActionAttribute 标记该方法不是Action。

Lookup Mechanism of Controller

From the above chapters, we know that MVC6 not only supports normal Controller (inherited from the subclass of Controller base class), but also supports Controller of POCO. In this section, we will study the lookup principle and mechanism of Controller under 1.

First, to determine whether a class is an Controller, you must first determine how many assemblies have such a class defined. Microsoft.AspNet.Mvc Namespace IAssemblyProvider Interface is to override and find all assemblies that may define Controller. The default implementation of this interface is DefaultAssemblyProvider Class, where the necessary condition is that Controller that defines MVC must refer to one or more of the following assemblies, as listed below:


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, if you define a reference Microsoft.AspNet.Mvc DLL class library, POCO Controller in it will be considered as Controller of MVC. In other words, if you define POCO Controller classes that do not refer to any one of these assemblies, then these Controller classes will not be considered Controller of MVC.

Lookup of assemblies

At present, there are two ways to customize the lookup mechanism of Controller. The first is inheritance IAssemblyProvider Realization CandidateAssemblies Method (or overload DefaultAssemblyProvider ) to define its own logic. The interface is defined as follows:


public interface IAssemblyProvider
{
 IEnumerable<Assembly> CandidateAssemblies { get; }
}

Another way, which may be relatively simpler, is to use IServicesCollection To define the assembly to find:


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

After using the above code, the system will put DefaultAssemblyProvider Switch to FixedSetAssemblyProvider To realize the above judgment mechanism, that is, to find in a fixed range of assemblies.

Filtering of assemblies

Once the assemblies have been identified, another question arises. How do you determine whether an assembly refers to the assemblies listed in the above MVC prerequisites? The answer is, Microsoft.Framework.Runtime In ILibraryManager Object of the interface instance GetReferencingLibraries Method to find out how many assemblies refer to one of the assemblies in the above list. For example, you can base on the Microsoft.AspNet.Mvc Assembly to find out how many assemblies refer to the assembly, as shown in the following example:


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

The code for using this function in the DefaultAssemblyProvider default 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);
}

Judgment of Controller

Once you have identified an assembly that meets the requirements, you can iterate through all the types in the assembly and then determine whether the type is Controller. In the new version of Controller judgment, it is 1 to realize this function IControllerTypeProvider Interface, which provides a ControllerTypes The read-only attribute is used to get all defined Controller, and the interface definition is as follows:


public interface IControllerTypeProvider
{
 IEnumerable<TypeInfo> ControllerTypes { get; }
}

DefaultControllerTypeProvider Is the default implementation of this interface. When querying the qualified Controller, the default implementation class defines 1 IsController Method is used 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) //  The type must be 1 Category 
 {
  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 1 A Public Class (and not nested), nested classes cannot be used as 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)) //  Class with Controller End, or inherit from Controller Base class, or its parent class is also Controller . 
 {
  return false;
 }
 if (typeInfo.IsDefined(typeof(NonControllerAttribute))) //  This class cannot be set NonControllerAttribute Characteristic 
 {
  return false;
 }

 return true;
}

You can also do it yourself IControllerTypeProvider Interface to define its own Controller decision logic, however, and to fix some assembly types, MVC in the IServicesCollection There is also an extension method provided on to restrict 1 Controller specific types, such as the following example:


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

After using the above code, the system will put DefaultControllerTypeProvider Switch to FixedSetControllerTypeProvider To implement the above judgment mechanism, that is, to restrict some specific classes as Controller, and other types as Controller.

Lookup Mechanism of Action

Action is selected by IActionSelector The default implementation class for the interface DefaultActionSelector To achieve, in the implementation of SelectAsync Method, the best matching Action is selected through context and routing data, with the following schematic code:


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

There is another place to determine whether a method is Action, and that is IActionModelBuilder Interface, the default implementation of which is DefaultActionModelBuilder Class, implemented as follows:


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

This implementation method is implemented through an internal IsAction Method to determine whether the method is a real Action method, the specific code is as follows:


public interface IAssemblyProvider
{
 IEnumerable<Assembly> CandidateAssemblies { get; }
}
0

The above content is about Controller and Action search related important code, detailed principle steps, please refer to Microsoft.AspNet.Mvc.Core All source code under the assembly.


Related articles: