среда, 13 июля 2011 г.

Roundup: ASP.NET MVC Request Processing Pipeline

ASP.NET MVC's request processing pipeline shares many things in common with traditional ASP.NET Web Forms for they both take advantage of IIS sits underlying. However, unlike ASP.NET Web Forms, MVC gives you much more flexibility, you can modify any piece to your own liking, and even rearrange or replace components outright. The most significant difference is the place where routing (of course you can do routing alike MVC in traditional ASP.NET Web Forms, but it's not by default) and controllers kick in, and filters extend your capability to a larger scale as well.
This article will guide you through this infinitely extensible request processing pipeline and mostly discuss the four major steps involved to help you grab the big idea and comprehend what is going on behind the scene.
Internet Information Services, as Microsoft's web server system, works as the backbone of ASP.NET request processing. When each HTTP request arrives the server, a kernel-mode Windows device driver called HTTP.SYS will first classify the request based on its requested URL, port and IP address combination and then forward it to a registered application such as an IIS web site.
Essentially, ASP.NET MVC is built upon ASP.NET core, ASP.NET must be enabled for its application pool to guarantee ASP.NET MVC to work properly. When you create such a site in IIS, an application pool properly setup shall be assigned which is a pool of ASP.NET worker threads to take care of HTTP requests. You could tinker with many settings for application pools such as its managed pipeline modes, but that aspect is distracting from this article's purpose and will not be discussed here.
When an incoming request reaches an ASP.NET enabled IIS web site, ASP.NET kicks in and a worker thread from the application pool is dispatched to cope with that request. It notifies each registered HTTP module that a new request is starting. HTTP modules are .NET classes which implement IHttpModule interface that you can plug into the ASP.NET request processing pipeline. They are commonly used to handle certain events such as routing and information logging in a request's life cycle.
One particularly important HTTP module is registered by default in any ASP.NET MVC application: UrlRoutingModule. This module is the beginning of the core routing system which plays the most important role in the second step of request processing pipeline. If you are working on MVC 2 targeting .NET 3.5, you will find this module registered in your web.config file. On the other hand with .NET 4, UrlRoutingModule is removed from web.config and is referenced in machine-wide configuration by default. To see it's setup and running, you can take a look at Modules under IIS tab for your site in Internet Information Services(IIS) Manager.
2. Core Routing
System.Web.Routing assembly is invoked when UrlRoutingModule gets involved in processing a request. The core job of routing is about to recognize and parse incoming URL, setting up a request context data structure that subsequent components can use whatever they wish such as ASP.NET MVC uses it to transfer control to the relevant MVC controller class and to supply action method parameters.
Core routing for ASP.NET MVC will first check if the incoming request's URL is towards a static file (e.g. an image file or a style sheet file) on the disk. If so, core routing will simply pass by and leave it to IIS. IIS will serve them back to clients directly so that the process can be done efficiently.
On the other hand, if the incoming URL doesn't target a static file on disk, for example, it's mapped to a MVC controller, the core routing system will investigate its active configuration to figure out how to handle that incoming URL.
How routing is directed is configured in a static collection called System.Web.Routing.RouteTable.Routes. Each entry in this collection represents a distinct URL pattern your application wish to accept, for instance, /Shop/Catalog/{ID}, and constraints which limit the range of acceptable values for each parameters. Additionally, each entry will supply a route handler - an object which implements IRouteHandler thus it can take over and process the request. The RouteTable.Routes collection in an ASP.NET MVC application is usually setup by adding code to a method called RegisterRoutes() in Global.asax file.
To match a certain incoming request, the core routing system looks through RouteTable.Routes collection from top to bottom, picking the first entry that matches the URL pattern. Having found the matching entry, routing transfers control to that entry's specified route handler, with a request context data structure that describes the route entry and parameters parsed from the URL. MVC framework then gets in on the action.
3. Controllers and Actions
Reaching this step, core routing system has selected a particular entry in RouteTable.Routes, and has parsed any routing parameters out of the URL and packed them inside a request context data structure. Now is the time for controllers and actions to enter the scene.
In general, for ASP.NET MVC applications, almost all route entries specify one particular route handler: MvcRouteHandler. It is the built-in default route handler for ASP.NET MVC and it knows how to take the request context data and invoke the corresponding controller class. It does so by using a controller factory object. By default, the DefaultControllerFactory is used, however it can be replaced by custom controller factory in case special concern is raised for example dependency injection needs.
When a corresponding controller is picked by the controller factory object, its Execute method is called. Execute is the only method defined in the IController interface and every controller class must implement such interface:
public interface IController
    void Execute(RequestContext requestContext);
Note that the requestContext parameter provides all the request context data constructed by the routing system, including parameters parsed out from the incoming URL request. It also provides access to the Request and Response objects.
In most of the cases, you will work with high-level controller classes which wrap and hide the low-level details of the Execute method defined in IController interface. Thus you basically won't write data to response stream directly. Instead, you return an action result that describes the intended output in your controllers. For instance, you return a ViewResult if the goal for such controller is to render a view, or a RedirectToRouteResult if you want redirect request to a different action method or a different controller. MVC framework will then take care of executing that result at the appropriate moment in the request processing pipeline.
Note that filters can be attached onto a controller class or an action method and inject extra logic that runs before or after action methods, or before or after action results are executed. Filters are extremely flexible and their logic can be run at many possible moment during this step of the request processing pipeline.
In a nut shell, controllers and actions are the central pillars of the whole ASP.NET MVC framework.
4. Action Results and Views
At this point, the MVC framework will ask the ActionResult object returned by your controller to execute. The ActionResult does whatever that type of ActionResult does, either return a string to browser, issue an HTTP redirection or the most fun part to render a view.
The ActionResult which does the job of rendering a view is namely ViewResult. This one is able to locate and render a particular view, passing along whatever ViewData structure the action method has constructed. It does so by calling a "view engine" (for example, web form view engine for MVC 2 and razor view engine for MVC 3) determined by the controller.
In traditional ASP.NET Web Forms, each web form ASPX page have a dedicated pipeline on its own, starting with on-the-fly ASPX/ASCX compilation and running through a series of events known as the page life cycle. ASP.NET MVC tells a different story here. Its view pages are simple and contain only UI logic. There is no code-behind files nor web form page life cycle is involved, which save you from the marsh from a smart UI design to a much better architecture imposes separation of concerns.
The following work flow diagram will help you better comprehend ASP.NET MVC request processing pipeline.

Комментариев нет: