- Modern Web Development with ASP.NET Core 3
- Ricardo Peres
- 1282字
- 2021-06-18 18:36:01
Using controllers
In MVC, a controller is responsible for handling requests. It is where the business logic is located, where data is retrieved, request parameters validated, and so on. In object-oriented languages, such as those that support .NET Framework, this is implemented in classes. Keep in mind that the MVC pattern advocates a strong separation of responsibilities, which makes all of its components particularly important; even given this fact, a controller is really the only required part of ASP.NET Core, as you can live without views. Just think of web services that do not return any user interface or models. This is a very important aspect of ASP.NET Core.
Controller base classes
ASP.NET Core (as with its predecessors) offers a base class called ControllerBase that you can inherit from, although it is not strictly necessary. We will discuss this in more detail later on in this chapter. However, inheriting from ControllerBasehas a few advantages:
Easy access to model validation
Helper methods to return different results (redirect, JSON, views, text, and more)
Direct access to the request and response infrastructure objects, including headers, cookies, and more
- Ability to intercept/override action events
In reality, there is another class, Controller, that in turn inherits from ControllerBase, which you should inherit from in case you want to work with views. A case where you wouldn't need to work with views would be if you are writing a web service (web API).
The templates in Visual Studio always generate controllers that inherit from the Controller class, but you can change them to POCOs if you like. The only real requirement, unless you want to change the convention, is to add the Controller suffix to all your controllers. The namespace or physical location is irrelevant—for example, you can create controllers in different folders or namespaces.
The ControllerBase class, among others, makes the following properties available:
- ControllerContext (ControllerContext): The execution context for the current controller and request, which includes the action descriptor (used to guess which action should be called) and value provider factories, from which the action parameters are obtained; it's an instance of the class.
- HttpContext (HttpContext): The HTTP context, which includes the request and response objects, from which we can obtain and set all headers, cookies, status codes, authentication information, certificates, and more; also provides access to the dependency injection (DI) framework, framework features, the session state (if it's enabled), and the underlying connection properties.
- MetadataProvider (IModelMetadataProvider): This is used to extract metadata—validators, textual descriptors, and editing information—for the class model.
- ModelBinderFactory (IModelBinderFactory): This is an object that is used to create the binders that, in turn, are used to bind submitted request properties to a given class model.
- ModelState (ModelStateDictionary): This is the submitted model's values and validation results.
- ObjectValidator (IObjectModelValidator): This is an instance that is used to validate the submitted model.
- Request (HttpRequest): This handles the convenience pointer to the same object inside the HttpContext.
- Response (HttpResponse): This handles the conveniencepointer to the same object inside theHttpContext.
- Url (IUrlHelper): This is an instance that enables convenience methods to generate URL links to specific controller actions.
- User (ClaimsPrincipal): This holds a reference to the current ASP.NET Core user; depending on the actual authentication mechanism in use, it will hold different values and claims, and even if it is not authenticated, this will never be null.
The Controller class offers all of the preceding properties plus view-specific properties:
- RouteData (RouteData): This contains the MVC route data parameters.
- ViewBag (dynamic): This is a dynamic collection of data to be made available in a view.
- ViewData (ViewDataDictionary): This is identical to ViewBag, but is strongly typed in the form of a key–value dictionary.
- TempData (ITempDataDictionary): This is a strongly typed dictionary for data to maintain until the next form submission.
Of course, your controller needs to offer at least one action method that can be used to perform an action and return something meaningful to the caller, be it an HTML view, some JSON content, or just an HTTP status code.
You also have a number of virtual methods that you can override so as to perform actions before, after, or instead of an action method being called. These are defined in the interfaces IActionFilter and IAsyncActionFilter, which are implemented by Controller:
- OnActionExecuted is called after an action is called.
- OnActionExecuting is called synchronously just before an action is called.
- OnActionExecutingAsync is called asynchronously before an action is called.
These interfaces are the bases of filters, which we will discuss in more detail later on.
POCO controllers
In ASP.NET Core, your controllers do not need to inherit from any base class or implement a particular interface. As we mentioned earlier, all they need is the Controller suffix, by convention, and to avoid the [NonController] attribute. The problem with this approach is that you lose all helper methods and context properties (HttpContext, ControllerContext, ViewBag, and Url), but you can have them injected. Let's see how this works.
Adding context to POCO controllers
Say for example, that you have a POCO controller, HomeController. You don't have the various context and view bag-related properties, but with a couple of attributes applied to appropriately typed properties, you can have the infrastructure inject them, as shown in the following example:
public class HomeController
{
private readonly IUrlHelperFactory _url;
public HomeController(IHttpContextAccessor ctx, IUrlHelperFactory url)
{
this.HttpContext = ctx.HttpContext;
this._url = url;
}
[ControllerContext]
public ControllerContext { get; set; }
public HttpContext HttpContext { get; set; }
[ActionContext]
public ActionContext ActionContext { get; set; }
[ViewDataDictionary]
public ViewDataDictionary ViewBag { get; set; }
public IUrlHelper Url { get; set; }
public string Index()
{
this.Url = this.Url ?? this._url.GetUrlHelper(this.ActionContext);
return "Hello, World!";
}
}
You will notice a few interesting things here:
- ActionContext, ControllerContext, and ViewBag are automatically injected just by adding the [ActionContext], [ControllerContext], and [ViewDataDictionary] attributes to properties of any name, and with the ActionContext, ControllerContext and ViewDataDictionary types, respectively.
- When the controller is instantiated by the ASP.NET Core infrastructure, the dependency injection framework injects the IHttpContextAccessor and IUrlHelperFactory objects.
- The HttpContext object needs to be obtained from the passed IHttpContextAccessor instance.
- In order to build an IUrlHelper, the IUrlHelperFactory needs an instance of ActionContext; because we don't have that at constructor time, we need to build it later on, for example, in an action method (in this example, Index).
However, to make this work, we need to tell ASP.NET Core to register the default implementations of IHttpContextAccessor and IUrlHelperFactory. This is normally done in the ConfigureServices method of the Startup class:
services.AddScoped<IHttpContextAccessor, HttpContextAccessor>();
//or, since version 2.1:
services.AddHttpContextAccessor();
services.AddScoped<IUrlHelperFactory, UrlHelperFactory>();
These properties will behave in exactly the same way as their non-POCO counterparts that are inherited from ControllerBase and Controller.
Intercepting actions in POCO controllers
If you want, you can also implement one of the filter interfaces so that you can interact with the request before or after an action is called, such as IActionFilter:
public class HomeController : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//before the action is called
}
public void OnActionExecuted(ActionExecutedContext context)
{
//after the action is called
}
}
If you prefer to have an asynchronous handler, implement the asynchronous version (IAsyncXXXFilter) instead. We will talk more about filters in Chapter 10, Understanding Filters.
Let's nowsee how controllers are discovered by the framework.
- UI圖標創意設計
- Drupal 8 Blueprints
- Hands-On Machine Learning with scikit:learn and Scientific Python Toolkits
- Learning Chef
- Photoshop智能手機APP UI設計之道
- DevOps for Networking
- C語言最佳實踐
- Groovy for Domain:specific Languages(Second Edition)
- Windows Presentation Foundation Development Cookbook
- Node.js Design Patterns
- Learning SciPy for Numerical and Scientific Computing(Second Edition)
- Training Systems Using Python Statistical Modeling
- 軟件測試分析與實踐
- Java與Android移動應用開發:技術、方法與實踐
- 計算機信息技術實踐教程