官术网_书友最值得收藏!

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.
It's safe and convenient to inherit from Controller, even if you do not use views; it won't cause any problems.

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.

I almost forgot: if a controller class has the [NonController] attribute applied to it, then it is not considered and cannot be used as a controller.

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.

If you add the [Controller] attribute to any POCO class, you can turn it into a controller, regardless of its name.
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.

主站蜘蛛池模板: 开远市| 徐州市| 惠州市| 依兰县| 潞城市| 高邑县| 洛隆县| 江阴市| 繁昌县| 云浮市| 武义县| 家居| 渭南市| 紫云| 昭通市| 佛学| 陕西省| 尚志市| 澳门| 勃利县| 长沙市| 余姚市| 东莞市| 潢川县| 嵊州市| 鸡西市| 拉萨市| 贵州省| 靖宇县| 永年县| 丹江口市| 嘉黎县| 武平县| 定陶县| 太保市| 额敏县| 潞城市| 沂源县| 道孚县| 阳信县| 夏津县|