An ASP.NET MVC controller is a class that inherits some core behavior from a base class. Inherited core behavior includes the ability to execute a request and to read and write from the temporary data storage—the TempData collection. The controller class exposes a number of public methods, each of which represents an action method, if not otherwise specified through the NonAction attribute.
You can further specialize the overall behavior of the controller by using a few declarative attributes that belong to three categories: filters, invocation attributes, and action selectors. In this article, I’ll cover filter attributes.
Available Filter Attributes
A filter attribute indicates a behavior that can be attached to a few predefined stages during the execution of an action method. Examples of filter attributes are Authorize, HandleError, OutputCache, ValidateAntiForgeryToken, and ValidateInput. Their effect on the controller class is summarized in Figure 1.
You can apply such filters to the controller class or to individual methods. When applied to the class as a whole they have an effect on all action methods exposed by the controller. Applying the HandleError attribute at the class level makes sense because it means that any exceptions thrown by any action methods of the class are automatically handled. Applying the Authorize attribute at the class level makes less sense because it would imply that you need authentication for each and every method you invoke on the controller. Note also that filter attributes are inherited by any derived class.
All attributes in Figure 1 are derived from a common base class—the FilterAttribute class. This class defines a property named Order. The Order property indicates the order in which multiple attributes will be applied. By default the Order property is assigned a value of -1, which means that the order for the filter is unspecified. However, any filter with an unspecified order is always executed before a filter with a fixed order. Let’s find out more about filter attributes.
The Authorize Attribute
You use the Authorize attribute when you want to make sure that only authorized users can gain access to a particular action method in the controller. You apply the filter as shown below:
public ActionResult Index()
In this case, the method Index executes only if the current user making the request is authenticated. For the user to be authenticated, an authentication cookie must be attached to the request.
The parameter-less Authorize attribute doesn’t perform any further check against the user name or role of the user. To enforce that only certain users or roles can invoke the method, you simply add more named parameters to the attribute. You can specify multiple roles or multiple users by using a separating comma. Here’s an example:
public ActionResult Index()
If a user is not authenticated, or doesn’t have the required user name or role, the authorization filter returns an HTTP 401 status code. However, this status code is never displayed to the end user making the request. Instead, if you attempt to invoke an action method without being authenticated and authorized, you are redirected to the login page. Why is this so?
Any ASP.NET MVC application relies on the standard FormsAuthentication HTTP module for forms-based authentication. As you may know, the HTTP module registers its own handler for the EndRequest global event and intercepts the end of the request that failed with an HTTP 401 code. The FormsAuthentication HTTP module is programmed to automatically redirect the browser to the login page if an HTTP 401 status code is detected.
Finally, note that the Authorize attribute doesn’t distinguish between non-logged users and logged users that do not have the rights to invoke a given action method. In both cases, the attempt to call the action method fails and the user is redirected to the login page. While you can always create a customized version of the Authorize attribute that distinguishes between non-authenticated and non-authorized users, I believe that the best way to deal with this problem is to prevent users from clicking where they are not allowed to. By enabling buttons and links only for authorized users you brilliantly and safely work around the limitations of Authorize.
The HandleError Attribute
You use the HandleError attribute when you want to set up a safety net to protect your controller, or just a particular method, from unhandled runtime exceptions. The HandlerError attribute tells the ASP.NET MVC framework that a custom error page should be displayed in lieu of the standard yellow-screen-of-death if anything goes wrong.
The default custom error page used in this case is error.aspx and is defined under the Views\Shared folder. Note that this error page can be overridden by another error.aspx page that you place in the controller-specific folder under the Views folder (Figure 2). The error.aspx page under the Views/Content folder replaces the error.aspx page under the Views\Shared folder for any unhandled exceptions thrown by methods of the Content controller class.
After you've attached the HandleError attribute to a method (or, more likely, to the whole controller class) you won’t notice any special behavior on your development machine if you haven’t also modified the web.config file to enable custom error pages:
With the default settings for the customErrors section only remote users would see a generic error page. Local users (i.e., developers) will be shown the classic error page with detailed information about the stack trace.
By default, the HandleError attribute may trap any exceptions during the execution of the action method, as well as during the subsequent rendering of the view. You can, however, restrict your control over only a few exceptions. All you need is the code below:
public ActionResult Index()
Note that the preceding code won’t trap unhandled exceptions beyond the two exception types explicitly listed. To trap any exceptions, you need the parameter-less attribute. However, by specifying an exception type you can also indicate a specific error page.
The OutputCache Attribute
The OutputCache attribute brings the output caching feature of classic ASP.NET into the context of ASP.NET MVC. The attribute lets you specify a variety of attributes including duration of the cache, cache profile, vary-by parameters, and location. Here’s how to use it:
public ActionResult Index()
The Duration parameter indicates in seconds how long the method’s response should stay cached in memory. The VaryByParam attribute indicates the number of distinct versions of the response you should cache—one for each distinct value of the specified property. If you use None, then you tell the system you don’t want multiple versions of the same method’s response.
Like the HandleError attribute, the OutputCache attribute is often applied at the class level and applies to every action method in the class.
The ValidateAntiForgeryToken Attribute
Probably a bit less popular than the notorious cross-site scripting attack (XSS), the Cross-Site Request Forgery (CSRF) attack is fairly easy to prepare and can be as disruptive as XSS. A CSRF attack consists of finding a victim who loads a fake page into her browser. Typically, the fake page contains hidden script code and markup that posts data to a particular server that the victim has an affinity with.
Because the post occurs from the victim’s computer, any authentication cookies on the machine are uploaded. If successful, a CSRF attack lets the hacker upload his own data to the remote server through the victim’s account. The hacker is then logged into the remote system on behalf of the victim and gains full control over the victim’s credentials.
ASP.NET MVC supplies a couple of tools to protect websites from this vulnerability. One is a helper method to generate some tailor-made HTML markup; the other is the ValidateAntiForgeryToken attribute. You might want to apply the ValidateAntiForgeryToken attribute to any action methods that work over the HTTP POST verb:
public ActionResult Edit(Customer customer)
The attribute contains code that kicks in during the authorization phase of an action method request. At this time, the attribute code ensures the posted request contains a cookie and a form field with a common fixed name. If anything is missing, an exception is thrown. Otherwise, the attribute ensures that the content of both the cookie and the input field match. Figure 3 shows an anti-forgery exception.
Who’s responsible for adding the security cookie and input field? That’s where the HTML helper method comes into play. In any view that may post some critical data to the server, add the following markup within a