Advanced Controllers

As the workhorse of the ASP.NET MVC stack, it's no surprise that the controller has a lot of advanced features that were way beyond the scope of Chapter 2. In this section, you'll learn both how the controller internals work and how you can use it in some advanced scenarios.

Defining the Controller: The IController Interface

Now that you have the basics down, we'll take a more structured look at exactly how controllers are defined and used. Up to this point, we've kept things simple by focusing on what a controller does; now it's time to look at what a controller is. To do that, you'll need to understand the IController interface. As discussed in Chapter 1, among the core focuses of ASP.NET MVC are extensibility and flexibility. When building software this way, it's important to leverage abstraction as much as possible by using interfaces.

For a class to be a controller in ASP.NET MVC, it must at minimum implement the IController interface, and by convention the name of the class must end with the suffix Controller. The naming convention is actually quite important — and you'll find that many of these small rules are in play with ASP.NET MVC, which will make your life just a little bit easier by not making you define configuration settings and attributes. Ironically, the IController interface is quite simple, given the power it is abstracting:

public interface IController
{
   void Execute(RequestContext requestContext);
}

It's a simple process, really: When a request comes in, the Routing system identifies a controller, and it calls the Execute method.

The point of the IController interface is to provide a very simple starting point for anyone who wants to hook his or her own controller framework into ASP.NET MVC. The Controller class, which is covered later in this chapter, layers much more interesting behavior on top of this interface. This is a common extensibility pattern within ASP.NET.

For example, if you're familiar with HTTP handlers, you might have noticed that the IController interface looks very similar to IHttpHandler:

public interface IHttpHandler
{
   void ProcessRequest(HttpContext context);

   bool IsReusable { get; }
}

Ignoring the IsReusable property for a moment, IController and IHttpHandler are pretty much equivalent in terms of responsibility. The IController.Execute and IHttpHandler.ProcessRequest methods both respond to a request and write some output to a response. The main difference between the two is the amount of contextual information provided to the method. The IController.Execute method receives an instance of RequestContext, which includes not just the HttpContext but also other information relevant to a request for ASP.NET MVC.

The Page class, which is probably the class most familiar to ASP.NET Web Forms developers because it is the default base class for an ASPX page, also implements IHttpHandler.

The ControllerBase Abstract Base Class

Implementing IController is pretty easy, as you've seen, but really all it's doing is providing a facility for Routing to find your controller and call Execute. This is the most basic hook into the system that you could ask for, but overall it provides little value to the controller you're writing. This may be a good thing to you — many custom tool developers don't like it when a system they're trying to customize imposes a lot of restrictions. Others may like to work a bit closer with the API, and for that there is ControllerBase.


Product Team Aside
Back in the early days the ASP.NET MVC product team debated removing the IController interface completely. Developers who wanted to implement that interface could use their own implementation of MvcHandler instead, which decidedly handles a lot of the core execution mechanics based on the request coming in from Routing.
We decided to leave it in, however, because other features of the ASP.NET MVC framework (IControllerFactory and ControllerBuilder) can work with the interface directly — which provides added value to developers.

The ControllerBase class is an abstract base class that layers a bit more API surface on top of the IController interface. It provides the TempData and ViewData properties (which are ways of sending data to a view, discussed in Chapter 3). The Execute method of ControllerBase is responsible for creating the ControllerContext, which provides the MVC-specific context for the current request much the same way that an instance of HttpContext provides the context for ASP.NET in general (providing request and response, URL, and server information, among elements).

This base class is still very lightweight and enables developers to provide extremely customized implementations for their own controllers, while benefiting from the action filter infrastructure in ASP.NET MVC (ways of filtering and working with request/response data, which are discussed in Chapter 13). What it doesn't provide is the ability to convert actions into method calls. That's where the Controller class comes in.

The Controller Class and Actions

In theory, you could build an entire site with classes that implement ControllerBase or IController, and it would work. Routing would look for an IController by name and then call Execute, and you would have yourself a very, very basic website.

This approach, however, is akin to working with ASP.NET using raw IHttpHandlers — it would work, but you're left to reinvent the wheel and plumb the core framework logic yourself.

Interestingly, ASP.NET MVC itself is layered on top of HTTP handlers, as you'll see later, and overall there was no need to make internal plumbing changes to ASP.NET to implement MVC. Instead, the ASP.NET MVC team layered this new framework on top of existing ASP.NET extensibility points.

The standard approach to writing a controller is to have it inherit from the System.Web.Mvc.Controller abstract base class, which implements the ControllerBase base class, and thus the IController interface. The Controller class is intended to serve as the base class for all controllers because it provides a lot of nice behaviors to controllers that derive from it.

Figure 15.6 shows the relationship between IController, ControllerBase, the Controller abstract base class, and the two controllers that are included in a default ASP.NET MVC 4 application.

Figure 15.16

images

Action Methods

All public methods of a class that derive from Controller are action methods, which are potentially callable via an HTTP request. Rather than one monolithic implementation of Execute, you can factor your controller into action methods, each of which responds to a specific user input.


Product Team Aside
Upon reading that every public method of your Controller class is publicly callable from the Web, you might have a gut reaction concerning the security of such an approach. The product team had a lot of internal and external debate concerning this.
Originally, each action method required that an attribute, ControllerActionAttribute, be applied to each callable method. However, many felt this violated the DRY principle (Don't Repeat Yourself). It turns out that the concern over these methods being web-callable has to do with a disagreement over what it means to opt in.
As far as the product team is concerned, multiple levels of opting in exist before a method is web-callable. The first level that you need to have opted in to is an ASP.NET MVC project. If you add a public Controller class to a standard ASP.NET Web Application project, that class is not going to suddenly be web-callable (although adding such a class to an ASP.NET MVC project is likely to make it callable). You would still need to define a route with a route handler (such as the MvcRouteHandler) that corresponds to that class.
The general consensus here is that by inheriting from Controller, you've opted in to this behavior. You can't do that by accident. Even if you did, you would still have to define routes that correspond to that class.

The ActionResult

As mentioned before, the purpose of the controller within the MVC pattern is to respond to user input. In ASP.NET MVC, the action method is the granular unit of response to user input. The action method is ultimately responsible for handling a user request and outputting the response that is displayed to the user, which is typically HTML.

The pattern that an action method follows is to do whatever work is asked of it, and at the end, return an instance of a type that derives from the ActionResult abstract base class.

Taking a quick look at the source for the ActionResult abstract base class, you see:

public abstract class ActionResult
{
  public abstract void ExecuteResult(ControllerContext context);
}

Notice that the class contains a single method, ExecuteResult. If you're familiar with the Command Pattern, this should look familiar to you. Action results represent commands that your action method wants the framework to perform on its behalf.

Action results generally handle framework-level work, while the action method handles your application logic. For example, when a request comes in to display a list of products, your action method will query the database and put together a list of the appropriate products to show. Perhaps it needs to perform some filtering based on business rules within your app. At this point, your action method is completely focused on application logic.

However, once the method is ready to display the list of products to the user, you may not want your code, which is focused on view logic, to have to worry about implementation details provided by the framework, such as writing to the HTTP response directly. Perhaps you have a template defined that knows how to format a collection of products as HTML. You'd rather not have that information encapsulated in the action method because it would violate the separation of concerns the authors have so carefully cultivated up until this point.

One technique you have at your disposal is to have the action method return a ViewResult (which derives from ActionResult) and give the data to that instance, and then return that instance. At that point, your action method is done with its work, and the action invoker will call the ExecuteResult method on that ViewResult instance, which does the rest. Here's what the code might look like:

public ActionResult ListProducts()
{
 //Pseudo code
 IList<Product> products = SomeRepository.GetProducts();
 ViewData.Model = products;
 return new ViewResult {ViewData = this.ViewData };
}

In practice, you'll probably never see code that instantiates an ActionResult instance directly like that. Instead, you would use one of the helper methods of the Controller class, such as the View method, as follows:

public ActionResult ListProducts()
{
 //Pseudo code
 IList<Product> products = SomeRepository.GetProducts();
 return View(products);
}

The next chapter covers the ViewResult in more depth and tells how it relates to views.

Action Result Helper Methods

If you take a close look at the default controller actions in the default ASP.NET MVC project template, you'll notice that the action methods don't directly instantiate instances of ViewResult. For example, here's the code for the About method:

public ActionResult About() {
   ViewData["Title"] = "About Page";
   return View();
}

Notice that it returns the result of a call to the View method. The Controller class contains several convenience methods for returning ActionResult instances. These methods are intended to help make action method implementations a bit more readable and declarative. Instead of creating new instances of action results, it is more common to return the result of one of these convenience methods.

These methods are generally named after the action result type that they return, with the Result suffix omitted. Hence the View method returns an instance of ViewResult. Likewise, the Json method returns an instance of JsonResult. The one exception in this case is the RedirectToAction method, which returns an instance of RedirectToRoute.

The Redirect, RedirectToAction, and RedirectToRoute methods all send an HTTP 302 status code, indicating a temporary redirection. In cases where content has moved permanently, you want to tell clients that you are using an HTTP 301 status code. One of the primary benefits of doing this is search engine optimization. When a search engine encounters an HTTP 301 code, it will update the URLs displayed in search results; updating expired links can often have an impact on search engine ranking as well. For this reason, each method that returns a RedirectResult has a counterpart method that returns an HTTP 301 status code. These counterpart methods are RedirectPermanent, RedirectToActionPermanent, and RedirectToRoutePermanent. Note that browsers and other clients will cache HTTP 301 responses, so you should not use them unless you are certain the redirect is permanent.

Table 15.4 lists the existing methods and which types they return.

Table 15.4 Controller Convenience Methods That Return ActionResult Instances

Method Description
Redirect Returns a RedirectResult, which redirects the user to the appropriate URL.
RedirectPermanent The same as Redirect but returns a RedirectResult with the Permanent property set to true, thus returning an HTTP 301 status code.
RedirectToAction Returns a RedirectToRouteResult, which redirects the user to an action using the supplied route values.
RedirectToActionPermanent The same as RedirectToAction but returns a RedirectResult with the Permanent property set to true, thus returning an HTTP 301 status code.
RedirectToRoute Returns a RedirectToRouteResult, which redirects the user to the URL that matches the specified route values.
RedirectToRoutePermanent The same as RedirectToRoute but returns a RedirectResult with the Permanent property set to true, thus returning an HTTP 301 status code.
Method Description
View Returns a ViewResult, which renders the view to the response.
PartialView Returns a PartialViewResult, which renders a partial view to the response.
Content Returns a ContentResult, which writes the specified content (string) to the response.
File Returns a class that derives from FileResult, which writes binary content to the response.
Json Returns a JsonResult containing the output from serializing an object to JSON.
JavaScript Returns a JavaScriptResult containing JavaScript code that is immediately executed when returned to the client.

Action Result Types

ASP.NET MVC includes several ActionResult types for performing common tasks, as listed in Table 15.5. Each type is discussed in more detail in the sections that follow.

Table 15.5 Descriptions of ActionResult Types

ActionResult Type Description
ContentResult Writes the specified content directly to the response as text.
EmptyResult Represents a null or empty response. It doesn't do anything.
FileContentResult Derives from FileResult and writes a byte array to the response.
FilePathResult Derives from FileResult and writes a file to the response based on a file path.
FileResult Serves as the base class for a set of results that writes a binary response to the stream. Useful for returning files to the user.
FileStreamResult Derives from FileResult and writes a stream to the response.
HttpNotFound Derives from HttpStatusCodeResult. Returns an HTTP 404 response code to the client, indicating that the requested resource is not found.
HttpStatusCodeResult Returns a user-specified HTTP code.
ActionResult Type Description
HttpUnauthorizedResult Derives from HttpStatusCodeResult. Returns an HTTP 401 response code to the client, indicating that the requestor does not have authorization to the resource at the requested URL.
JavaScriptResult Used to execute JavaScript code immediately on the client sent from the server.
JsonResult Serializes the objects it is given into JSON and writes the JSON to the response, typically in response to an Ajax request.
PartialViewResult This is similar to ViewResult, except it renders a partial view to the response, typically in response to an Ajax request.
RedirectResult Redirects the requestor to another URL by returning either a temporary redirect code 302 or permanent redirect code 301, depending upon a Boolean Permanent flag.
RedirectToRouteResult Similar to RedirectResult, but redirects the user to a URL specified via Routing parameters.
ViewResult Calls into a view engine to render a view to the response.

ContentResult

The ContentResult writes its specified content (via the Content property) to the response. This class also allows for specifying the content encoding (via the ContentEncoding property) and the content type (via the ContentType property).

If the encoding is not specified, the content encoding for the current HttpResponse instance is used. The default encoding for HttpResponse is specified in the globalization element of web.config.

Likewise, if the content type is not specified, the content type set on the current HttpResponse instance is used. The default content type for HttpResponse is text/html.

EmptyResult

As the name implies, the EmptyResult is used to indicate that the framework should do nothing. This follows a common design pattern known as the Null Object pattern, which replaces null references with an instance. In this instance, the ExecuteResult method has an empty implementation. This design pattern was introduced in Martin Fowler's book Refactoring: Improving the Design of Existing Code (Addison-Wesley Professional, 1999). You can learn more at http://martinfowler.com/bliki/refactoring.html.

FileResult

The FileResult is very similar to the ContentResult except that it is used to write binary content (for example, a Microsoft Word document on disk or the data from a blob column in SQL Server) to the response. Setting the FileDownloadName property on the result will set the appropriate value for the Content-Disposition header, causing a file download dialog to appear for the user.

Note that FileResult is an abstract base class for three different file result types:

  • FilePathResult
  • FileContentResult
  • FileStreamResult

Usage typically follows the factory pattern in which the specific type returned depends on which overload of the File method (discussed later) is called.

HttpStatusCodeResult

The HttpStatusCodeResult provides a way to return an action result with a specific HTTP response status code and description. For example, to notify the requestor that a resource is permanently unavailable, you could return a 410 (Gone) HTTP status code. Suppose you'd made the firm decision that your store would stop carrying disco albums. You could update your StoreController Browse action to return a 410 if a user searched for disco:

public ActionResult Browse(string genre)
{
      if(genre.Equals("disco",StringComparison.InvariantCultureIgnoreCase))
            return new HttpStatusCodeResult(410);

      var genreModel = new Genre { Name = genre };
   return View(genreModel);
}

Note that there are five specific ActionResults based on common HTTP status codes, which were previously described in Table 15.5:

  • HttpNotFoundResult
  • HttpStatusCodeResult
  • HttpUnauthorizedResult
  • RedirectResult
  • RedirectToRouteResult

Both RedirectResult and RedirectToRouteResult (described later in this section) are based on the common HTTP 301 and HTTP 302 response codes.

JavaScriptResult

The JavaScriptResult is used to execute JavaScript code on the client sent from the server. For example, when using the built-in Ajax helpers to make a request to an action method, the method could return a bit of JavaScript that is immediately executed when it gets to the client:

public ActionResult DoSomething() {
   script s = "$('#some-div').html('Updated!'),";
   return JavaScript(s);
}

This would be called by the following code:

    <%: Ajax.ActionLink("click", "DoSomething", new AjaxOptions()) %>
   <div id="some-div"></div>

This assumes that you've referenced the Ajax libraries and jQuery.

JsonResult

The JsonResult uses the JavaScriptSerializer class to serialize its contents (specified via the Data property) to the JSON (JavaScript Object Notation) format. This is useful for Ajax scenarios that have a need for an action method to return data in a format easily consumable by JavaScript.

As for ContentResult, the content encoding and content type for the JsonResult can both be set via properties. The only difference is that the default ContentType is application/json and not text/html for this result.

Note that the JsonResult serializes the entire object graph. Thus, if you give it a ProductCategory object, which has a collection of 20 Product instances, every Product instance will also be serialized and included in the JSON sent to the response. Now imagine if each Product had an Orders collection containing 20 Order instances. As you can imagine, the JSON response can grow huge quickly.

There is currently no way to limit how much to serialize into the JSON, which can be problematic with objects that contain a lot of properties and collections, such as those typically generated by LINQ to SQL. The recommended approach is to create a type that contains the specific information you want included in the JsonResult. This is one situation in which an anonymous type comes in handy.

For example, in the preceding scenario, instead of serializing an instance of ProductCategory, you can use an anonymous object initializer to pass in just the data you need, as the following code sample demonstrates:

public ActionResult PartialJson()
{
      var category = new ProductCategory { Name="Partial"};
      var result = new {
            Name = category.Name,
            ProductCount = category.Products.Count
      };
      return Json(result);
}

In this example, all you needed was the category name and the product count for the category. Rather than serializing the entire object graph, you pulled the information you needed from the actual object and stored that information in an anonymous type instance named result. You then sent that instance to the response, rather than the entire object graph. Another benefit of this approach is that you won't inadvertently serialize data you don't want the client to see, such as any internal product codes, stock quantity, supplier information, and so forth.

RedirectResult

The RedirectResult performs an HTTP redirect to the specified URL (set via the Url property). Internally, this result calls the HttpResponse.Redirect method, which sets the HTTP status code to HTTP/1.1 302 Object Moved, causing the browser to immediately issue a new request for the specified URL.

Technically, you could just make a call to Response.Redirect directly within your action method, but using the RedirectResult defers this action until after your action method finishes its work. This is useful for unit testing your action method and helps keep underlying framework details outside of your action method.

RedirectToRouteResult

RedirectToRouteResult performs an HTTP redirect in the same manner as the RedirectResult, but instead of specifying a URL directly, this result uses the Routing API to determine the redirect URL.

Note that there are two convenience methods (defined in Table 15.4) that return a result of this type: RedirectToRoute and RedirectToAction.

As discussed earlier, there are three additional methods that return an HTTP 301 (Moved Permanently) status code: RedirectPermanent, RedirectToActionPermanent, and RedirectToRoutePermanent.

ViewResult

The ViewResult is the most widely used action result type. It calls the FindView method of an instance of IViewEngine, returning an instance of IView. The ViewResult then calls the Render method on the IView instance, which renders the output to the response. In general, this inserts the specified view data (the data that the action method has prepared to be displayed in the view) into a view template that formats the data for displaying.

PartialViewResult

PartialViewResult works in exactly the same way that ViewResult does, except that it calls the FindPartialView method to locate a view rather than FindView. It's used to render partial views and is useful in partial update scenarios when using Ajax to update a portion of the page with new HTML.

Implicit Action Results

One constant goal with ASP.NET MVC, and software development in general, is to make the intentions of the code as clear as possible. There are times when you have a very simple action method only intended to return a single piece of data. In this case, it is helpful to have your action method signature reflect the information that it returns.

To highlight this point, consider a Distance method which calculates the distance between two points. This action could write directly to the response — as shown in the first controller actions in Chapter 2, in the section titled “Writing Your First (Outrageously Simple) Controller.” However, an action that returns a value can also be written as follows:

public double Distance(int x1, int y1, int x2, int y2)
{
   double xSquared = Math.Pow(x2 - x1, 2);
   double ySquared = Math.Pow(y2 - y1, 2);
   return Math.Sqrt(xSquared + ySquared);
}

Notice that the return type is a double and not a type that derives from ActionResult. This is perfectly acceptable. When ASP.NET MVC calls that method and sees that the return type is not an ActionResult, it automatically creates a ContentResult containing the result of the action method and uses that internally as the ActionResult.

One thing to keep in mind is that the ContentResult requires a string value, so the result of your action method needs to be converted to a string first. To do this, ASP.NET MVC calls the ToString method on the result, using InvariantCulture, before passing it to the ContentResult. If you need to have the result formatted according to a specific culture, you should explicitly return a ContentResult yourself.

In the end, the preceding method is roughly equivalent to the following method:

public ActionResult Distance(int x1, int y1, int x2, int y2)
{
   double xSquared = Math.Pow(x2 - x1, 2);
   double ySquared = Math.Pow(y2 - y1, 2);
   double distance = Math.Sqrt(xSquared + ySquared);
   return Content(Convert.ToString(distance, CultureInfo.InvariantCulture));
}

The advantages of the first approach are that it makes your intentions clearer, and the method is easier to unit test.

Table 15.6 highlights the various implicit conversions you can expect when writing action methods that do not have a return type of ActionResult.

Table 15.6 Implicit Conversions with Action Methods

Return Value Description
Null The action invoker replaces null results with an instance of EmptyResult. This follows the Null Object Pattern. As a result, implementers writing custom action filters don't have to worry about null action results.
Void The action invoker treats the action method as if it returned null, and thus an EmptyResult is returned.
Other objects that don't derive from ActionResult The action invoker calls ToString using InvariantCulture on the object and wraps the resulting string in a ContentResult instance.
Note
The code to create a ContentResult instance is encapsulated in a virtual method on the action invoker called CreateActionResult. For those who want to return a different implicit action result type, you can write a customer action invoker that derives from ControllerActionInvoker and override that method.
One example might be to have return values from action methods automatically be wrapped by a JsonResult.

Action Invoker

We've made several references in this chapter to the action invoker without giving any details about it. Well, no more arm waving! This section covers the role of a critical element in the ASP.NET MVC request processing chain: the thing that actually invokes the action you're calling — the action invoker. When we first defined the controller earlier in this chapter, we looked at how Routing maps a URL to an action method on a Controller class. Diving deeper into the details, you learned that routes themselves do not map anything to controller actions; they merely parse the incoming request and populate a RouteData instance stored in the current RequestContext.

It's the ControllerActionInvoker, set via the ActionInvoker property on the Controller class that is responsible for invoking the action method on the controller based on the current request context. The invoker performs the following tasks:

  • It locates the action method to call.
  • It gets values for the parameters of the action method by using the model binding system.
  • It invokes the action method and all its filters.
  • It calls ExecuteResult on the ActionResult returned by the action method. For methods that do not return an ActionResult, the invoker creates an implicit action result as described in the previous section and calls ExecuteResult on that.

In the next section, you'll take a closer look at how the invoker locates an action method.

How an Action Is Mapped to a Method

The ControllerActionInvoker looks in the route values dictionary associated with the current request context for a value corresponding to the action key. As an example, here is the URL pattern for the default route:

{controller}/{action}/{id}

When a request comes in and matches that route, a dictionary of route values (accessible via the RequestContext) is populated based on this route. For example, if a request comes in for

/home/list/123

Routing adds the value list with a key of action to the route values dictionary.

At this point within the request, an action is just a string extracted from the URL; it is not a method. The string represents the name of the action that should handle this request. Though it may commonly be represented by a method, the action really is an abstraction. There might be more than one method that can respond to the action name. Or it might not even be a method but a workflow or some other mechanism that can handle the action.

The point of this is that, while in the general case an action typically maps to a method, it doesn't have to. We'll see an example of this later in the chapter, where we discuss asynchronous actions where there are two methods per action.

Action Method Selection

Once the invoker has determined the action's name, it attempts to identify a method that can respond to that action. By default, the invoker uses reflection to find a public method on a class that derives from a Controller that has the same name (case-insensitive) as the current action. Such a method must meet the following criteria:

  • An action method must not have the NonActionAttribute defined.
  • Special methods such as constructors, property accessors, and event accessors cannot be action methods.
  • Methods originally defined on Object (such as ToString) or on Controller (such as Dispose or View) cannot be action methods.

Like many features of ASP.NET MVC, you can tweak this default behavior to suit any special needs your applications might have.

ActionNameAttribute

Applying the ActionNameAttribute attribute to a method allows you to specify the action that the method handles. For example, suppose that you want to have an action named View. Unfortunately, this would conflict with the built-in View method of Controller that's used to return a ViewResult. An easy way to work around this issue is to do the following:

[ActionName("View")]
public ActionResult ViewSomething(string id)
{
 return View();
}

The ActionNameAttribute redefines the name of this action as View. Thus, this method is invoked in response to requests for /home/view, but not for /home/viewsomething. In the latter case, as far as the action invoker is concerned, an action method named ViewSomething does not exist.

One consequence of this is that if you're using our conventional approach to locate the view that corresponds to this action, the view should be named after the action, not after the method. In the preceding example (assuming that this is a method of HomeController), you would look for the view in ∼/Views/Home/View.cshtml by default.

This attribute is not required for an action method. There is an implicit rule that the name of the action method serves as the action name if this attribute is not applied.

ActionSelectorAttribute

You're not done matching the action to a method yet. Once you've identified all methods of the Controller class that match the current action name, you need to whittle the list down further by looking at all instances of the ActionSelectorAttribute applied to the methods in the list.

This attribute is an abstract base class for attributes that provide fine-grained control over which requests an action method can respond to. The API for this method consists of a single method:

public abstract class ActionSelectorAttribute : Attribute
{
 public abstract bool IsValidForRequest(ControllerContext controllerContext,
    MethodInfo methodInfo);
}

At this point, the invoker looks for any methods in the list that contain attributes that derive from this attribute and calls the IsValidForRequest method on each attribute. If any attribute returns false, the method that the attribute is applied to is removed from the list of potential action methods for the current request.

At the end, you should be left with one method in the list, which the invoker then invokes. If more than one method can handle the current request, the invoker throws an exception indicating that there is an ambiguity in the method to call. If no method can handle the request, the invoker calls HandleUnknownAction on the controller.

The ASP.NET MVC framework includes two implementations of this base attribute: the AcceptVerbsAttribute and the NonActionAttribute.

AcceptVerbsAttribute

AcceptVerbsAttribute is a concrete implementation of ActionSelectorAttribute that uses the current HTTP request's HTTP method (verb) to determine whether or not a method is the action that should handle the current request. This allows you to have method overloads, both of which are actions but respond to different HTTP verbs.

MVC includes a more terse syntax for HTTP method restriction with the [HttpGet], [HttpPost], [HttpDelete], [HttpPut], and [HttpHead] attributes. These are simple aliases for the previous [AcceptVerbs(HttpVerbs.Get)], [AcceptVerbs(HttpVerbs.Post)], [AcceptVerbs(HttpVerbs.Delete)], [AcceptVerbs(HttpVerbs.Put)], and [AcceptVerbs(HttpVerbs.Head)] attributes, but are easier to both type and read.

For example, you may want two versions of the Edit method: one that renders the edit form and the other that handles the request when that form is posted:

[HttpGet]
public ActionResult Edit(string id)
{
 return View();
}

[HttpPost]
public ActionResult Edit(string id, FormCollection form)
{
 //Save the item and redirect…
}

When a POST request for /home/edit is received, the action invoker creates a list of all methods of the controller that match the edit action name. In this case, you would end up with a list of two methods. Afterward, the invoker looks at all the ActionSelectorAttribute instances applied to each method and calls the IsValidForRequest method on each. If each attribute returns true, the method is considered valid for the current action.

For example, in this case, when you ask the first method if it can handle a POST request, it will respond with false because it handles only GET requests. The second method responds with true because it can handle the POST request, and it is the one selected to handle the action.

If no method is found that meets these criteria, the invoker will call the HandleUnknownAction method on the controller, supplying the name of the missing action. If more than one action method meeting these criteria is found, an InvalidOperationException is thrown.

Simulating RESTful Verbs

Most browsers support only two HTTP verbs during normal web browsing: GET and POST. However, the REST architectural style also makes use of a few additional standard verbs: DELETE, HEAD, and PUT. ASP.NET MVC allows you to simulate these verbs via the Html.HttpMethodOverride helper method, which takes a parameter to indicate one of the standard HTTP verbs (DELETE, GET, HEAD, POST, and PUT). Internally, this works by sending the verb in an X-HTTP-Method-Override form field.

The behavior of HttpMethodOverride is complemented by the [AcceptVerbs] attribute as well as the new shorter verb attributes:

  • HttpPostAttribute
  • HttpPutAttribute
  • HttpGetAttribute
  • HttpDeleteAttribute
  • HttpHeadAttribute

Though the HTTP method override can be used only when the real request is a POST request, the override value can also be specified in an HTTP header or in a query string value as a name/value pair.


More on Overriding HTTP Verbs
Overriding HTTP verbs via X-HTTP-Method-Override is not an official standard, but it has become a common convention. It was first introduced by Google as part of the Google Data Protocol in 2006 (http://code.google.com/apis/gdata/docs/2.0/basics.html), and has since been implemented in a variety of RESTful web APIs and web frameworks. Ruby on Rails follows the same pattern, but uses a _method form field instead of X-HTTP-Method-Override.
MVC allows the override for POST requests only. The framework will look for the overridden verb first from the HTTP headers, then from post values, and, finally, from query string values.

Invoking Actions

Next the invoker uses the model binder (discussed in depth in Chapter 4, in the “Model Binding” section) to map values for each parameter of the action method, and is then finally ready to invoke the action method itself. At this point, the invoker builds up a list of filters associated with the current action method and invokes the filters along with the action method, in the correct order. For more detailed coverage of this, see the “Action Filters” section of Chapter 13.

Using Asynchronous Controller Actions

ASP.NET MVC 2 and later include full support for an asynchronous request pipeline. The purpose of this asynchronous pipeline is to allow the web server to handle long-running requests — such as those that spend a large amount of time waiting for a network or database operation to complete — while still remaining responsive to other requests. In this regard, asynchronous code is about servicing requests more efficiently than it is about servicing an individual request more quickly.

While very powerful, writing asynchronous controller actions prior to MVC 4 was difficult. MVC 4 leverages the following recent .NET Framework features to greatly simplify the process:

  • .NET 4 introduced a new Task Parallel Library to simplify the development work to support parallelism and concurrency in .NET applications. The Task Parallel Library includes a new type, the Task, to represent an asynchronous operation. MVC 4 supports this by allowing you to return Task<ActionResult> from an action method.
  • .NET 4.5 further simplifies asynchronous programming through two new keywords, async and await. The async modifier notifies the compiler that a method (including anonymous methods and lambda expressions) is asynchronous, containing one or more long-running operations. The await keyword is applied to tasks within an asynchronous method, indicating that the method should be suspended until the awaited task completes.
  • The combination of .NET 4 Tasks and .NET 4.5 async and await support is referred to as the Task-based Asynchronous Pattern, or TAP. Writing asynchronous controller actions using TAP support in MVC 4 is significantly easier than the prior solution in MVC 2 and 3. In this section, we'll focus on using TAP with MVC 4 on .NET 4.5, with a few notes at the end if you're working in MVC 2 or 3.

To understand the difference between asynchronous and synchronous ASP.NET code, one must first have a basic knowledge of how requests are processed by the web server. IIS maintains a collection of idle threads (the thread pool) that are used to service requests. When a request comes in, a thread from the pool is scheduled to process that request. While a thread is processing a request, it cannot be used to process any other requests until it has finished with the first. The ability of IIS to service multiple requests simultaneously is based on the assumption that there will be free threads in the pool to process incoming requests.

Now consider an action that makes a network call as part of its execution, and consider that the network call might take two seconds to complete. From the site visitor's point of view, the server takes about two seconds to respond to his or her request, if you take into account a little bit of overhead on the web server itself. In a synchronous world, the thread processing the request is blocked for the two seconds that the network call is taking place. That is, the thread cannot perform useful work for the current request because it's waiting for the network call to complete, but it also can't do any useful work for any other request because it's still scheduled to work on the first request. A thread in this condition is known as a blocked thread. Normally this isn't a problem because the thread pool is large enough to accommodate such scenarios. However, in large applications that process multiple simultaneous requests, this can lead to many threads being blocked waiting for data and not enough idle threads left in the thread pool available for dispatch for servicing new incoming requests. This condition is known as thread starvation, and it can severely affect the performance of a website (see Figure 15.17).

In an asynchronous pipeline, threads are not blocked waiting for data. When a long-running application such as a network call begins, the action is responsible for voluntarily relinquishing control of the thread for the duration of the operation. Essentially, the action tells the thread, “It'll be a while before I can continue, so don't bother waiting for me right now. I'll notify IIS when the data I need is available.” The thread is then returned to the thread pool so that it can handle another request, and the current request is essentially paused while waiting for data. Importantly, while a request is in this state, it is not assigned to any thread from the thread pool, so it is not blocking other requests from being processed. When the action's data becomes available, the network request completion event notifies IIS and a free thread from the thread pool is dispatched to continue processing the request. The thread that continues processing the request may or may not be the same thread that originated the request, but the pipeline takes care of this so that developers don't have to worry about it (see Figure 15.18).

It is important to note that in the previous example, the end user still sees a two-second delay between the time he sends the request and the time he receives a response from the server. This is what is meant by the earlier statement about asynchronous being primarily for efficiency rather than the response speed for an individual request. Even though it takes the same amount of time to respond to the user's request regardless of whether the operation is synchronous or asynchronous, in an asynchronous pipeline the server is not blocked from doing other useful work while waiting for the first request to complete.

Choosing Synchronous versus Asynchronous Pipelines

The following are some guidelines for deciding whether to use synchronous or asynchronous pipelines. Note that these are just guidelines and each application will have its own requirements.

Use synchronous pipelines when:

  • The operations are simple or short-running.
  • Simplicity and testability are important.
  • The operations are CPU-bound rather than IO-bound.

Use asynchronous pipelines when:

  • Testing shows that blocking operations are bottlenecking site performance.
  • Parallelism is more important than simplicity of code.
  • The operations are IO-bound rather than CPU-bound.

Because asynchronous pipelines have more infrastructure and overhead than synchronous pipelines, asynchronous code is somewhat more difficult to reason about than synchronous code. Testing such code would require mocking more of the infrastructure, and it would also require taking into account that the code can execute in many different orderings. Finally, it's not really beneficial to convert a CPU-bound operation to an asynchronous operation, because all that does is add overhead to an operation that probably wasn't blocked to begin with. In particular, this means that code that performs CPU-bound work within the ThreadPool.QueueUserWorkItem() method will not benefit from an asynchronous pipeline.

Writing Asynchronous Action Methods

Asynchronous actions using the new TAP model in MVC 4 are very similar to standard (synchronous) actions. Here are the requirements for converting an action to an asynchronous action:

  • The action method must be marked as asynchronous using the async modifier.
  • The action must return either Task or Task<ActionResult>.
  • Any asynchronous operations within the method use the await keyword to suspend operation until the call has completed.

For example, consider a portal site that displays news for a given area. The news in this example is provided via a GetNews() method that involves a network call that could be long-running. A typical synchronous action might look like this:

public class PortalController : Controller {
   public ActionResult News(string city) {
       NewsService newsService = new NewsService();
       NewsModel news = newsService.GetNews(city);
       return View(news);
   }
}

Here is that same action converted to an asynchronous action:

public class PortalController : Controller {
   public async Task<ActionResult> News(string city) {
       NewsService newsService = new NewsService();
       NewsModel news = await newsService.GetNews(city);
       return View(news);
   }
}

As described earlier, we only had to make three changes: add the async modifier to the action, return a Task<ActionResult>, and add an await before the call to the long-running service.


When Would You Just Return Task?
You may have wondered why MVC 4 supports returning Task as well as Task<ActionResult>. What's the point of an action that doesn't return anything?
It turns out that this is pretty useful in long-running service operations that don't need to return any output. For instance, you might have an action that performs a lengthy service operation, such as sending bulk e-mail or building a large report. In those cases, there's nothing to return and there's no caller listening. Returning Task is the same as returning void from a synchronous action; both are converted to an EmptyResult response, which means no response is sent.

Performing Multiple Parallel Operations

The preceding example won't perform any faster than a standard synchronous action; it just allows for more efficient use of server resources (as explained at the beginning of this section). One of the greatest benefits of asynchronous code can be seen when an action wants to perform several asynchronous operations at a time. For example, a typical portal site would show not only news, but also sports, weather, stocks, and other information:

public class PortalController : Controller {
   public ActionResult Index(string city) {
       NewsService newsService = new NewsService();      
       WeatherService weatherService = new WeatherService();
       SportsService sportsService = new SportsService();

       PortalViewModel model = new PortalViewModel {
           News = newsService.GetNews(city),
           Weather = weatherService.GetWeather(city),
           Sports = sportsService.GetScores(city)
       };
       return View(model);
   }
}

Note that the calls are performed sequentially, so the time required to respond to the user is equal to the sum of the times required to make each individual call. If the calls are 200, 300, and 400 milliseconds (ms), then the total action execution time is 900 ms (plus some insignificant overhead).

Similarly, an asynchronous version of that action would take the following form:

public class PortalController : Controller {
   public async Task<ActionResult> Index(string city) {
       NewsService newsService = new NewsService();      
       WeatherService weatherService = new WeatherService();
       SportsService sportsService = new SportsService();

       var newsTask = newsService.GetNewsAsync(city);
       var weatherTask = weatherService.GetWeatherAsync(city);
       var sportsTask = sportsService.GetScoresAsync(city);

       await Task.WhenAll(newsTask, weatherTask, sportsTask);

       PortalViewModel model = new PortalViewModel {
           News = newsTask.Result,
           Weather = weatherTask.Result,
           Sports = sportsTask.Result
       };

       return View(model);
   }
}

Note that the operations are all kicked off in parallel, so the time required to respond to the user is equal to the longest individual call time. If the calls are 200, 300, and 400 ms, then the total action execution time is 400 ms (plus some insignificant overhead).


Parallel Task Calls using Task.WhenAll
Note that we used the Task.WhenAll() method to execute multiple tasks in parallel. You might think that just adding the await keyword to each of our service calls would parallelize them, but that's not the case. While await does release the thread until a long-running call completes, the second awaited call won't start until the first completes. Task.WhenAll will execute all tasks in parallel and will return when all tasks are complete.

In both of the preceding examples, the URL to access the action is /Portal/Index?city=Seattle (or /Portal?city=Seattle, using the default route), and the view page name is Index.cshtml (because the action name is Index).

This is a classic example where async is used not only for efficiency, but for performance as well (from the end user's perspective).

MVC 2 and 3 Using AsyncController

The Task-based Asynchronous Pattern requires MVC 4 and .NET 4.5. If you need to add asynchronous actions to an application running on MVC 2 or 3, you can make use of the AsyncController base class. In much the same way that the Controller type serves as the base class for synchronous controllers, the AsyncController type serves as the base class for asynchronous controllers. Converting a synchronous action to an asynchronous action using an AsyncController requires the following:

1. Instead of deriving the controller from Controller, derive it from AsyncController. Controllers that derive from AsyncController enable ASP.NET to process asynchronous requests, and they can still service synchronous action methods.
2. Create two methods for the action. The method that initiates the asynchronous process must have a name that consists of the action and the suffix “Async.” The method that is invoked when the asynchronous process finishes (the callback method) must have a name that consists of the action and the suffix “Completed.” In the previous example, the News method would have two methods: NewsAsync and NewsCompleted.

The NewsAsync method returns void (no value in Visual Basic). The NewsCompleted method returns an ActionResult instance. Although the action consists of two methods, it is accessed using the same URL as for a synchronous action method (for example, Portal/News?city=Seattle). Methods such as RedirectToAction and RenderAction will also refer to the action method as News, not as NewsAsync.

The parameters that are passed to NewsAsync use the normal parameter-binding mechanisms. The parameters that are passed to NewsCompleted use the Parameters dictionary.

3. Replace the synchronous call in the original ActionResult method with an asynchronous call in the asynchronous action method. In the example above, the call to newsService.GetHeadlines is replaced with a call to newsService.GetHeadlinesAsync.

Consider our original synchronous controller action with a single service call to a GetNews()method which involves a network call which could be long-running:

public class PortalController : Controller {
   public ActionResult News(string city) {
       NewsService newsService = new NewsService();
       NewsModel news = newsService.GetNews(city);
       return View(news);
   }
}

An asynchronous implementation of this action using the AsyncController base class would appear as follows:

public class PortalController : AsyncController {
   public void NewsAsync(string city) {
       AsyncManager.OutstandingOperations.Increment();
       NewsService newsService = new NewsService();
       newsService.GetNewsCompleted += (sender, e) => {
           AsyncManager.Parameters["news"] = e.News;
           AsyncManager.OutstandingOperations.Decrement();
       };
       newsService.GetNewsAsync(city);
   }

   public ActionResult NewsCompleted(NewsModel news) {
       return View(news);
   }
}

Note a few patterns here:

  • Asynchronous controller's base class is AsyncController rather than Controller. This tells the MVC pipeline to allow asynchronous requests.
  • Instead of a single News() action method there are two methods: NewsAsync() and NewsCompleted(), with the second method returning an ActionResult. This method pair is logically seen as a single action News, so it is accessed using the same URL as the synchronous action: /Portal/News?city=Seattle.
  • Observe the parameters passed to each method. The parameters passed to NewsAsync() are provided using the normal parameter binding mechanisms, while the parameters passed to NewsCompleted() are provided using the AsyncManager.Parameters dictionary. The NewsService consumed by the NewsAsync() method is an example of a service that exposes methods using an event-based asynchronous pattern (http://msdn.microsoft.com/en-us/library/wewwczdw.aspx).
  • Using AsyncManager.OutstandingOperations notifies the MVC pipeline of how many operations are pending completion. This is necessary because MVC otherwise has no way of knowing what operations were kicked off by the action method or when those operations are complete. When this counter hits zero, the MVC pipeline completes the overall asynchronous operation by calling the NewsCompleted() method.

Similarly, a parallel asynchronous version of our second example from above that calls into multiple long-running services would take the following form:

public class PortalController : AsyncController {
   public void IndexAsync(string city) {
       AsyncManager.OutstandingOperations.Increment(3);

       NewsService newsService = new NewsService();
       newsService.GetNewsCompleted += (sender, e) => {
           AsyncManager.Parameters["news"] = e.News;
           AsyncManager.OutstandingOperations.Decrement();
       };
       newsService.GetNewsAsync(city);

       WeatherService weatherService = new WeatherService();
       weatherService.GetWeatherCompleted += (sender, e) => {
           AsyncManager.Parameters["weather"] = e.Weather;
           AsyncManager.OutstandingOperations.Decrement();
       };
       weatherService.GetWeatherAsync(city);
       SportsService sportsService = new SportsService();
       sportsService.GetScoresCompleted += (sender, e) => {
           AsyncManager.Parameters["sports"] = e.Scores;
           AsyncManager.OutstandingOperations.Decrement();
       };
       SportsModel sportsModel = sportsService.GetScoresAsync(city);
   }

   public ActionResult IndexCompleted(NewsModel news,
       WeatherModel weather, SportsModel sports) {

       PortalViewModel model = new PortalViewModel {
           News = news,
           Weather = weather,
           Sports = sports
       };

       return View(model);
   }
}

Okay, I admit it: Part of the reason I showed the code for asynchronous actions based on AsyncController was to show how much easier it is now in MVC 4 using the Task-based Asynchronous Pattern!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset