Attributes of a Controller

Discover how to specialize the behavior of an ASP.NET MVC controller

Dino Esposito

February 15, 2010

9 Min Read
ITPro Today logo in a gray background | ITPro Today

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:

\[Authorize\]
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:

\[Authorize(Roles="admin", Users="DinoE")\]
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 ViewsShared 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 ViewsShared 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:

\[HandleError(ExceptionType=typeof(NullReferenceException), View="SyntaxError")\]
\[HandleError(ExceptionType=typeof(InvalidOperationException), View="InternalError")\]
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:

\[OutputCache(Duration=10, VaryByParam="None")\]
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:

\[AcceptVerbs(HttpVerbs.Post)\]
\[ValidateAntiForgeryToken\]
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

tag:

<% = Html.AntiForgeryToken() %>

The Html.AntiForgeryToken method creates a cookie on your machine and adds a hidden field to the form. If the action method target of the form is decorated with the ValidateAntiForgeryToken attribute, the content of the cookie and input field will be checked before authorizing the action method.

An action method can be publicly invoked because anybody on the Internet can invoke the URL that triggers the method. You can certainly require that the caller be authenticated, but there’s not much you can do if the hacker causes a legitimate user to post malicious data on his behalf.

The anti-forgery barrier is not a silver bullet, however. It requires cookies enabled on the client machine and protects only against POST attacks. In addition, other attacks such as XSS can be conducted to exploit other holes in your security, thus invalidating the anti-forgery barrier.

The ValidateInput Attribute
In ASP.NET, if you attempt to enter HTML tags in a form field, when submitting it you will inevitably incur a request validation exception. And the same occurs in ASP.NET MVC. In classic ASP.NET, this feature is controlled via the ValidateRequest Boolean property that you can set on a per page basis via the @Page directive.

The built-in validation layer for request content is certainly not a silver bullet and many times it has been more of a problem than a lifesaver. It's not uncommon that developers will disable automatic request input validation and replace it with a made-to-measure custom validation layer.

In ASP.NET MVC, though, none of the classic ASP.NET disable request validation techniques work. As an alternative, you're given the ValidateInput attribute:

\[AcceptVerbs(HttpVerbs.Post)\]
\[ValidateInput(false)\]
public ActionResult Edit(Customer customer)
\{
   :
\}

The preceding code disables any built-in input validation on the content being posted to the Edit action method. It's safe to disable automatic input validation only if you add your own validation layer for input data.

Getting Control
The execution of an action method passes through a number of steps and you can apply a number of filters to alter and specialize these steps. The five filter attributes I examined in this article let you control authorization for methods, output caching, exception handling, and other more specific aspects such as inserting an anti-forgery token and disabling an automatic request input validation.

In addition to these built-in filters, you can create your own custom filters by deriving from the abstract class ActionFilter. Possible examples of custom action filters are a component that logs the method’s execution or perhaps a component that applies GZIP compression to any response sent out by a given action method.


Dino Esposito ([email protected]) is a trainer and consultant who specializes mainly in ASP.NET, AJAX, and RIA solutions. Dino co-authored the best-seller Microsoft .NET: Architecting Applications for the Enterprise (Microsoft Press, 2008) and the just-released Programming ASP.NET MVC (Microsoft Press, 2009).

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like