CHAPTER 11

image

Filters

In our Web API applications, we might have some special logic that needs to run at specific points in the request processing stage, yet implementing this logic in every single place that we want to run it can lead to an undesirable result in our code base. In the first place, this action breaks our DRY (Don’t Repeat Yourself) principle. Filters come into play at this point; they allow us to register our custom logic and have it run at specific points.

In this chapter, we will take a closer look at filters in ASP.NET Web API by discovering how they are implemented and registered and how they behave.

Overview and Filter Processing Model

In ASP.NET Web API, as we have seen in Chapter 9, System.Web.Http.ApiController is the default implementation of the System.Web.Http.IHttpController. Using ApiController provides many flexibilities in terms of application structure, and one of them is the ability to use filters. If we are working with ApiController as the base class of controllers, we can register different types of filters and inject custom logic at certain points of the request processing stage.

Filters are often .NET attributes that implement certain interfaces but they definitely don’t have to be attributes. The reason why they are often attributes is to enable them to be easily applicable to individual controllers or even controller methods. There are three different types of filters inside the framework, as shown in Table 11-1.

Table 11-1. Four Different Filter Types Inside the ASP.NET Web API Framework

Filter Type Interface Description
Authorization IAuthorizationFilter The first filter; it runs before any other filters and is used for authorizing the request.
Action IActionFilter A type of filter that runs before the controller action is invoked.
Exception IExceptionFilter A type of filter that runs when an exception occurs.

In this chapter, the details of these filter types will be explained separately. When you look at the interfaces, you’ll see that all of them implement the IFilter interface, which has only one public read-only property: AllowMultiple. This property determines whether a filter can be applied multiple times to a scope.

These three types of filters have their execution order inside the processing pipeline. The authorization run first, before any other filters and the controller action. Just before the controller action is invoked, action filters are aggregated and executed. At last, if any exception has occurred inside the controller or the filters, registered exceptions filters are invoked.

On the other hand, every filter has a scope behind the scenes depending on where it is registered; this scope information determines the order in which the same filter types run. The scope of a filter is indicated by the FilterScope enum (or enumeration) type, which has the implementation shown in Listing 11-1.

Listing 11-1.  System.Web.Http.Filters.FilterScope

public enum FilterScope {
    Global = 0,
    Controller = 10,
    Action = 20
}

By looking at the FilterScope enum object, you can see that filters applied globally run before the others (lowest numbers first). Filters applied to controller classes run before the controller action level filters but after the global filters. Finally, action level filters run last, after all other filters.

image Note  The scope of a filter doesn’t change the filter type execution order. For example, if you have an authorization filter applied to your controller and an action filter applied globally, the authorization filter will run first. The filter scope determines only the execution order for the same types of filters.

Use Cases

In our sample application, we used all kinds of filters to make our application more compact and properly functional. Especially in cases where the same logic needs to be run multiple times, filters have their place. For example, let’s assume that we need to write a trace information every time a controller action runs. Listing 11-2 shows a controller implementation without filters.

Listing 11-2.  CarsController Without Filters

public class CarsController : ApiController {

    private readonly CarsContext _carsContext = new CarsContext();

    public IEnumerable<Car> GetCars() {

        log("GetCars");

        return _carsContext.All;
    }

    public Car GetCar(int id) {

        log("GetCar");

        return _carsContext.GetSingle(car => car.Id == id);
    }

    private void log(string actionName) {
        
        Trace.TraceInformation(
            string.Format(
                "Controller {0}, Action {1} is running...", "Cars", actionName
            )
        );
    }
}

CarsContext class here provides the data needed for our sample (Listing 11-3)

Listing 11-3.  Car and CarsContext Classes

public class Car {

    public int Id { get; set; }
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
    public float Price { get; set; }
}

public class CarsContext {

    private static int _nextId = 9;
    private static object _incLock = new object();

    // data store
    readonly static ConcurrentDictionary<int, Car> _carsDictionary =
        new ConcurrentDictionary<int, Car>(
        new HashSet<KeyValuePair<int, Car>> {
            new KeyValuePair<int, Car>(
                1,
                new Car {
                    Id = 1,
                    Make = "Make1",
                    Model = "Model1",
                    Year = 2010,
                    Price = 10732.2F
                }
            ),
            new KeyValuePair<int, Car>(
                  2,
                  new Car {
                      Id = 2,
                      Make = "Make2",
                      Model = "Model2",
                      Year = 2008,
                      Price = 27233.1F
                  }
            ),

            //Lines omitted for brevity . . .
    });

    public IEnumerable<Car> All {
        get {
            return _carsDictionary.Values;
        }

    }

    public IEnumerable<Car> Get(Func<Car, bool> predicate) {

        return _carsDictionary.Values.Where(predicate);
    }

    public Tuple<bool, Car> GetSingle(int id) {

        Car car;
        var doesExist = _carsDictionary.TryGetValue(id, out car);
        return new Tuple<bool, Car>(doesExist, car);
    }

    public Car GetSingle(Func<Car, bool> predicate) {

        return _carsDictionary.Values.FirstOrDefault(predicate);
    }

    public Car Add(Car car) {

        lock (_incLock) {

            car.Id = _nextId;
            _carsDictionary.TryAdd(car.Id, car);
            _nextId++;
        }

        return car;

    }

    public bool TryRemove(int id) {

        Car removedCar;
        return _carsDictionary.TryRemove(id, out removedCar);
    }

    public bool TryUpdate(Car car) {

        Car oldCar;
        if (_carsDictionary.TryGetValue(car.Id, out oldCar)) {

            return _carsDictionary.TryUpdate(car.Id, car, oldCar);
        }

        return false;
    }
}

Notice that we are running the same code for logging functionality inside every controller action by passing a relevant parameter to the private log method. Imagine that this needs to be done inside every single action of all application controllers. However, the same result can be achieved by using filters without repeating ourselves. Listing 11-4 shows a sample action filter implementation that generates the same logic.

Listing 11-4.  CarsController with Filters

using System;
using System.Web.Http.Filters;
using System.Threading.Tasks;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Threading;
using System.Diagnostics;

namespace Overview.Filters {

    [AttributeUsage(
        AttributeTargets.Class | AttributeTargets.Method)]
    public class LoggerAttribute : Attribute, IActionFilter {

        public Task<HttpResponseMessage> ExecuteActionFilterAsync(
            HttpActionContext actionContext,
            CancellationToken cancellationToken,
            Func<Task<HttpResponseMessage>> continuation) {

            var controllerCtx = actionContext.ControllerContext;

            Trace.TraceInformation(
                "Controller {0}, Action {1} is running...",
                controllerCtx.ControllerDescriptor.ControllerName,
                actionContext.ActionDescriptor.ActionName
            );

            //the way of saying everything is OK
            return continuation();
        }

        public bool AllowMultiple {
            get {
                return false;
            }
        }
    }
}

When this filter is applied to CarsController, you should see the same result but with better implementation. However, if you need this filter to run for all controller actions, register the filter as a global filter inside the Global.asax file (see Listing 11-5).

Listing 11-5.  Registering LoggerAttribute as a Global Filter

protected void Application_Start(object sender, EventArgs e) {

    //Lines omitted for brevity

    GlobalConfiguration.Configuration.Filters.Add(
        new LoggerAttribute());
}

image Note  Filter registration will be explained in detail in the “Registering Filters” section.

The capabilities of filters are not limited to these types of scenarios. For example, you can intercept a request with filters and cancel the further execution inside the processing pipeline. Authorization filters are often used for types of scenarios where there is a need to validate the request according to the authorization logic and cancel further execution steps by returning a custom HttpResponseMessage instance.

THE DIFFERENCE BETWEEN FILTERS AND MESSAGE HANDLERS

At first glance, it might seem that filters and message handlers serve the same purpose. In some cases, they do; still, they have very distinct characteristics.

The message handlers are invoked very early inside the pipeline, even before the controller selector is invoked, and they are invoked for every request. Using a message handler is a great choice to run logics that have to be executed for every request, including authentication, as you’ll see in Chapter 15. Also, by providing continuations inside the message handlers (as you saw in Chapter 10), you get a last chance to examine the response and perform some specific actions before the response goes out through the ASP.NET Web API pipeline.

The filters, on the other hand, have their own distinct purposes, as you might guess from their types. For example, exception filters are invoked only if an exception occurs inside the controller processing pipeline and authorization filters are invoked first, before any other filter types. In addition, filters can be invoked globally, per controller or per action. An excellent example of this is the AuthorizeAttribute, which is an authorization filter. AuthorizeAttribute checks against the Thread.CurrentPrincipal, on the basis of provided user names and roles, to decide whether or not the request is authorized. With AuthorizeAttribute, you can be very specific in deciding which controller or action allows access to which users or roles.

Default Filter Implementations

Besides filter interfaces, the Web API framework offers default implementation classes of those filters. These abstract classes are intended to make it easier to create custom filters, especially when we don’t need to perform any asynchronous processing. Table 11-2 shows the three base classes we’ll be using in the rest of this chapter.

Table 11-2. Default Filter Implementations

Interface Implementation Class Description
IAuthorizationFilter AuthorizationFilterAttribute Provides OnAuthorization method to authorize the request and handles returning generic Task object properly.
IactionFilter ActionFilterAttribute Unlike the IActionFilter interface, this class provides two methods: OnActionExecuting runs before the action is invoked, and OnActionExecuted runs just after the action is executed.
IExceptionFilter ExceptionFilterAttribute Provides the OnException method that will be invoked when an exception occurs.

All of those classes implement the relevant interfaces and are derived from the FilterAttribute class. FilterAttribute is an abstract class; it implements the IFilter interface and is derived from the Attribute class. FilterAttribute also properly sets the AllowMultiple property according to the AttributeUsage attribute’s AllowMultiple property.

Filter Execution Order

As was mentioned earlier, filter types have an execution order relative to each other; but besides that, filter execution order depends on other factors. One of them is the scope of a filter. The filter types have an execution order depending on their scope. For example, if an action Filter A is registered at the controller level and action Filter B is registered at the action level, the Filter A OnActionExecuting method will always run before the Filter B OnActionExecuting method.

If you use the ActionFilterAttribute abstract class as your base class to create action filters, action filters have two methods, as seen in Table 11-2. One is the OnActionExecuting method; the other is OnActionExecuted. As you know, OnActionExecuting runs before the controller action is invoked, and OnActionExecuted runs just after the action method has been executed.

On the other side, when the system runs the filters, they are run in different orders, depending on which method is being executed. For example, IActionFilter.OnActionExecuting filters run in forward order, while IActionFilter.OnActionExecuted filters run in reverse. Let’s look at an example to see these behaviors in action.

Here are two action filters: LoggerAttribute (Listing 11-6) and SecondaryLoggerAttribute (Listing 11-7).

Listing 11-6.  LoggerAttribute Implementation

public class LoggerAttribute : ActionFilterAttribute {

    private const string _loggerName = "Logger";

    public override void OnActionExecuting(
        HttpActionContext actionContext) {

        var controllerCtx = actionContext.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuting",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionContext.ActionDescriptor.ActionName
        );
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext) {

        var actionCtx = actionExecutedContext.ActionContext;
        var controllerCtx = actionCtx.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuted",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionCtx.ActionDescriptor.ActionName
        );
    }
}

Listing 11-7.  SecondaryLoggerAttribute Implementation

public class SecondaryLoggerAttribute : ActionFilterAttribute {

    private const string _loggerName = "SecondaryLogger";

    public override void OnActionExecuting(
        HttpActionContext actionContext) {

        var controllerCtx = actionContext.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuting",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionContext.ActionDescriptor.ActionName
        );
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext) {

        var actionCtx = actionExecutedContext.ActionContext;
        var controllerCtx = actionCtx.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuted",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionCtx.ActionDescriptor.ActionName
        );
    }
}

LoggerUtil here is a very simple class; it has only one static method to keep our Logger implementation clean (see Listing 11-8).

Listing 11-8.  LoggerUtil Class

public class LoggerUtil {

    public static void WriteLog(
        string loggerName, string loggerMethodName,
        string controllerName, string actionName) {

        var logFormat =
            "{0}, {1} method for Controller {2}, Action {3} is running...";

        Trace.TraceInformation(
            logFormat,
            loggerName,
            loggerMethodName,
            actionName,
            controllerName);
    }
}

Inside our controller the action filters are registered, as shown in Listing 11-9.

Listing 11-9.  CarsController

[Logger]
public class CarsController : ApiController {

    [SecondaryLogger]
    public string[] GetCars() {

        return new string[] {
            "Car 1",
            "Car 2",
            "Car 3",
            "Car 4"
        };
    }
}

The expected behavior here is to see the Logger action filter’s OnActionExecuting method run first and then the SecondaryLogger’s corresponding method run after it. However, the order should be reversed for the OnActionExecuted methods. When the application is run in debug mode, you should see the output result in the Output window (see Figure 11-1).

9781430247258_Fig11-01.jpg

Figure 11-1 .  Action filters output result to Output window

Exception filters run in reverse order as well. For example, Filter A and Filter B are exception filters. Filter A is registered at the controller level, Filter B at the action level. Normally, Filter A should run before Filter B, but as these are exception filters, Filter B will always run before Filter A.

In addition to these filter behaviors, note that filters may be skipped in some situations. For instance, if the requested is terminated by an action filter, the upcoming action filters won’t be run. At the same time, if there is any other action that has already been executed before the action filter that terminated the request, that action filter’s OnActionExecuted method will be run, but the others won’t.

Let’s look at an example to see this behavior in action. There are now three Logger action filters with the same implementations as Listings 11-6 and 11-7. One of these three filters has a slightly different implementation (see Listing 11-10).

Listing 11-10.  LoggerAttribute Action Filter That Will Terminate the Request

public class LoggerAttribute : ActionFilterAttribute {

    private const string _loggerName = "Logger";

    public override void OnActionExecuting(
        HttpActionContext actionContext) {

        //terminate the request by setting a new response
        //to actionContext.Response
        actionContext.Response = new HttpResponseMessage(
            HttpStatusCode.NotFound
        );
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext) {

        var actionCtx = actionExecutedContext.ActionContext;
        var controllerCtx = actionCtx.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuted",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionCtx.ActionDescriptor.ActionName
        );
    }
}

We have registered the Logger action filter at the controller level. At the same time, we have the GlobalLogger action filter registered as a global filter (see Listing 11-11) and the SecondaryLogger action filter applied at the controller action level (see Listing 11-12).

Listing 11-11.  GlobalLogger Action Filter

public class GlobalLoggerAttribute : ActionFilterAttribute {

    private const string _loggerName = "GlobalLogger";

    public override void OnActionExecuting(
        HttpActionContext actionContext) {

        var controllerCtx = actionContext.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuting",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionContext.ActionDescriptor.ActionName
        );
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext) {

        var actionCtx = actionExecutedContext.ActionContext;
        var controllerCtx = actionCtx.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuted",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionCtx.ActionDescriptor.ActionName
        );
    }
}

Listing 11-12.  SecondaryLogger Action Filter

public class SecondaryLoggerAttribute : ActionFilterAttribute {

    private const string _loggerName = "SecondaryLogger";

    public override void OnActionExecuting(
        HttpActionContext actionContext) {

        var controllerCtx = actionContext.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuting",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionContext.ActionDescriptor.ActionName
        );
    }

    public override void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext) {

        var actionCtx = actionExecutedContext.ActionContext;
        var controllerCtx = actionCtx.ControllerContext;

        LoggerUtil.WriteLog(
            _loggerName,
            "OnActionExecuted",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionCtx.ActionDescriptor.ActionName
        );
    }
}

So GlobalLogger filter should run first, and the Logger filter second. When the request is terminated by the Logger action filter’s OnActionExecuting method, then the Logger filter’s OnActionExecuted method shouldn’t be run in addition to the SecondaryLogger action filter. Since the GlobalLogger filter’s OnActionExecuting method has already been executed at this point, the system will run its OnActionExecuted method as well (see Figure 11-2).

9781430247258_Fig11-02.jpg

Figure 11-2 .  Action filter’s output result to Output window

The same behavior can been seen if the Logger action filter throws an exception instead of terminating the request by setting a new response message.

Registering Filters

Filters can be registered in any of several different ways. This section of the chapter will look at those options.

Action and Controller-Based Filters

Filters can be registered at the controller or action level and this allows control of individual pieces of the framework in terms of filter execution. We generally implement filters as .NET attributes in order to attach them easily at the class and method level, the levels corresponding to controller and action in our case.

.NET ATTRIBUTES IN A NUTSHELL

In the .NET framework, attributes are special classes derived from the abstract System.Attribute class; they provide metadata about the objects, methods, properties, and fields. When an attribute is associated with a program entity, that attribute can be queried and read at runtime with the help of reflection. In C#, attributes are registered using square brackets, and their public properties can be used, as well (e.g., [FooAttribute(FooBar = "Foo")]). By convention, all attribute names end with the word “Attribute” to distinguish them from other items in the .NET Framework, but the Attribute suffix can be omitted when using attributes in code. For example, an attribute class named FooAttribute can be designated [Foo] when it’s declared.

If a filter is registered at the controller level (as shown in Listing 11-13), the filter will be applied to all action methods under that controller.

Listing 11-13.  Registering a Filter at the Controller Level

[MyFilter]
public class CarsController : ApiController {

    public string[] GetCars() {
        
        //Lines omitted for brevity
    }
    
    public string[] GetCar(int id) {
    
        //Lines omitted for brevity
    }
}

Doing the same thing for an individual action will ensure that the attached filter will be executed only for that action. For example, the MyFilter filter will be applied only to the GetCars action of CarsController (cf. Listing 11-14).

Listing 11-14.  Registering a Filter at the Action Level

public class CarsController : ApiController {

    [MyFilter]
    public string[] GetCars() {
        
        //Lines omitted for brevity
    }
    
    public string[] GetCar(int id) {
    
        //Lines omitted for brevity
    }
}

Global Filters

Sometimes a filter—an exception filter, for example—might be a good fit for every controller action in an application. Luckily, you don’t have to register it manually for every single controller. The system allow registration of global filters using GlobalConfiguration’s static Configuration property inside Application_Start method in the Global.asax file.

Listing 11-15 shows MyFilter filter registered globally so that it will be applied to every controller action in our application.

Listing 11-15.  Registering a Filter Globally

protected void Application_Start(object sender, EventArgs e) {

    //Lines omitted for brevity

    GlobalConfiguration.Configuration.Filters.Add(new MyFilter());
}

Types of Filters

In ASP.NET Web API framework, there are three different types of filters: action filters, authorization filters and exception filters. This section will explain each of them with samples.

Action Filters

Action filters allow you to inject code to run before the action is invoked and after it is executed. An action filter needs to implement the IActionFilter interface. We’ll be working with the ActionFilterAttribute class (see Listing 11-16), the default implementation of IActionFilter.

Listing 11-16.  ActionFilterAttribute Abstract Class

public abstract class ActionFilterAttribute :
    FilterAttribute, IActionFilter {

    public virtual void OnActionExecuted(
        HttpActionExecutedContext actionExecutedContext);

    public virtual void OnActionExecuting(
        HttpActionContext actionContext);
}

The OnActionExecuting method is the one that runs just before the action is invoked. Inside this method, the framework passes a context class named HttpActionContext, which carries the information related to the request, including controller descriptor, action descriptor, request message, and the like. This context class is used by other filter types as well. Table 11-3 shows the public properties of HttpActionContext class.

Table 11-3. HttpActionContext Class Properties

Name Type Description
ActionArguments Dictionary<string, object> Dictionary  object; it holds the action method arguments.
ActionDescriptor HttpActionDescriptor Carries the action method details: action name, action method, binding details, etc.
ControllerContext HttpControllerContext Carries the controller information: controller type, controller name, controller instance, etc.
ModelState ModelStateDictionary Represents the model state, which has been populated by model binders.
Request HttpRequestMessage Represents  the request message.
Response HttpResponseMessage Represents the response message of the request. If this property is set to an instance of an HttpResponseMessage, the further execution of the request will be canceled.

The OnActionExecuted method, which runs after the action is executed, is provided with a context class, HttpActionExecutedContext, which carries an HttpActionContext instance, response message details generated by the action, and exception details, if there are any. The HttpActionExecutedContext class is also provided inside the exception filters. Table 11-4 shows the public properties of the HttpActionExecutedContext class.

Table 11-4. HttpActionExecutedContext Class Properties

Name Type Description
HttpActionContext HttpActionContext Provides an instance of the HttpActionContext object.
Exception Exception Provides the exception details, if there are any.
Request HttpRequestMessage Represents the request message.
Response HttpResponseMessage Provides the response message instance that has been populated so far. The response message can be altered; doing so will cause the returned response message to hold the alterations.

Here’s an example to show you how an action filter works.

Action Filter ModelState Validation Sample

Our applications often require us to validate incoming inputs according to our domain logic. For example, a data column inside our database system may not store a string value whose length is greater than 20. If this is the case and we receive an input for this object with a length greater than 20, we will most probably receive an error, depending on the database system, while trying to insert the value. To prevent these scenarios, some sort of validation logic is needed to respond properly if there is a validation error. Luckily, ASP.NET Web API supports validation with data annotation validation attributes along with action parameter binding.

image Note  Don’t worry! We will explain the details of validation in Chapter 13 and the action parameter binding feature of ASP.NET Web API in Chapter 12.

Let’s assume that we handle POST requests in our Web API application to create new car entities in our car gallery database. Our Car POCO class is shown in Listing 11-17. As you may notice, some properties, such as Required, have validation attributes.

Listing 11-17.  Car Class with Data Annotation Validation Attributes

public class Car {

    public int Id { get; set; }

    [Required]
    [StringLength(20)]
    public string Make { get; set; }

    [Required]
    [StringLength(20)]
    public string Model { get; set; }

    public int Year { get; set; }

    public float Price { get; set; }
}

For this example, we’ve created a class that acts like our data layer. What it does is manipulate a static collection (see Listing 11-18).

Listing 11-18.  CarsContext Class

public class CarsContext {

    private static int _nextId = 9;
    private static object _incLock = new object();

    // data store
    readonly static ConcurrentDictionary<int, Car> _carsDictionary =
        new ConcurrentDictionary<int, Car>(
        new HashSet<KeyValuePair<int, Car>> {
            new KeyValuePair<int, Car>(
                1,
                new Car {
                    Id = 1,
                    Make = "Make1",
                    Model = "Model1",
                    Year = 2010,
                    Price = 10732.2F
                }
            ),
            new KeyValuePair<int, Car>(
                  2,
                  new Car {
                      Id = 2,
                      Make = "Make2",
                      Model = "Model2",
                      Year = 2008,
                      Price = 27233.1F
                  }
            ),

            //Lines omitted for brevity . . .
    });

    public IEnumerable<Car> All {
        get {
            return _carsDictionary.Values;
        }    }

    public IEnumerable<Car> Get(Func<Car, bool> predicate) {
        return _carsDictionary.Values.Where(predicate);
    }

    public Car GetSingle(Func<Car, bool> predicate) {

        return _carsDictionary.Values.FirstOrDefault(predicate);
    }

    public Car Add(Car car) {

        lock (_incLock) {

            car.Id = _nextId;
            _carsDictionary.TryAdd(car.Id, car);
            _nextId++;
        }

        return car;
    }

}

With this implementation, if the properties are not valid according to validation attributes and we try to add the object to the collection anyway, there’s not going to be a problem, since it’s just an in-memory store. However, we would be working with a database system in a real-world scenario to store the data, that can be problematic if we try to insert a null value inside a column that doesn’t accept null values. So we need to be able to prevent this up front in our application.

Let’s see our controller implementation first (Listing 11-19).

Listing 11-19.  CarsController Implementation

public class CarsController : ApiController {

    private readonly CarsContext _carsContext = new CarsContext();

    public IEnumerable<Car> GetCars() {

        return _carsContext.All;
    }

    public Car GetCar(int id) {

        return _carsContext.GetSingle(car => car.Id == id);
    }

    public HttpResponseMessage PostCar(Car car) {

        _carsContext.Add(car);

        return new HttpResponseMessage(HttpStatusCode.Created);
    }
}

Inside the POST method, we will be receiving the Car object as a parameter and that Car will be populated by the proper HttpParameterBinding implementation. The FormatterParameterBinding, which is going to be selected for this case if the default configuration is in place, also validates the object according to validation attributes and populates the controller ModelState, which is a type of ModelStateDictionary, with error messages if there are any.

As has been seen earlier, we have access to a ModelStateDictionary instance through the HttpActionContext class, which is passed as a parameter to the OnActionExecuting method. So we can analyze the ModelState to see if it contains any validation errors. On the other hand, the OnActionExecuting method runs before the action is invoked. There is a chance to cancel the further execution of the request and set your own response message inside this method if you want to. This gives you the ability to cancel the request execution by setting your own response if there is any validation error that ModelState holds. To enable you to follow this approach, Listing 11-20 shows an action filter named ValidateModelStateAttribute.

Listing 11-20.  ValidateModelStateAttribute Action Filter

[AttributeUsage(
    AttributeTargets.Class | AttributeTargets.Method,
    AllowMultiple = false, Inherited = true)]
public class ValidateModelStateAttribute : ActionFilterAttribute {

    public override void OnActionExecuting(
        HttpActionContext actionContext) {

        if (!actionContext.ModelState.IsValid) {

            actionContext.Response =
                actionContext.Request.CreateErrorResponse(
                    HttpStatusCode.BadRequest,
                    actionContext.ModelState);
        }
    }
}

image Note  CreateErrorResponse is an HttpRequestMessage extension method; to be able to use it, you need to bring up the System.Net.Http namespace.

The steps being taken inside the ValidateModelStateAttribute filter are fairly simple. First of all is to check whether the ModelState is valid or not by examining its IsValid property. If it is valid, it’s left just as it is.

If the ModelState is not valid, you can create a new HttpResponseMessage by using the CreateErrorResponse extension method of HttpRequestMessage. One of the overloads of the CreateErrorResponse method accepts the ModelStateDictionary value as parameter and serializes it to the proper format, based on the content negotiation logic. This allows the server to process the content negotiation on your behalf. By doing it this way, you can send the response message with the format that the client wants—assuming the registered formatters are capable of doing it. Chapter 12 will cover formatters in detail.

If the ModelState is not valid, the ValidateModelStateAttribute will notify the framework not to continue processing the planned execution. The framework will do what you want here: return your response without executing the controller action method.

You now need to register this filter somehow. In our example that’s done at the action level (recall that filter registration was covered in detail in an earlier section, “Registering Filters”). Listing 11-21 modifies the PostCar method from Listing 11-19.

Listing 11-21.  Registering the ValidateModelStateAttribute Action Filter

//Lines omitted for brevity

[ValidateModelState]
public HttpResponseMessage PostCar(Car car) {

    _carsContext.Add(car);

    return new HttpResponseMessage(HttpStatusCode.Created);
}

//Lines omitted for brevity

To see it in action, use Fiddler to send a request to the API. First, let’s see if it works when we send a valid request to our API (see Figure 11-3).

9781430247258_Fig11-03.jpg

Figure 11-3 .  Sending a valid POST request using Fiddler

Once the request is sent, a 201 response should be returned. It indicates that the entry has been created successfully (see Figure 11-4).

9781430247258_Fig11-04.jpg

Figure 11-4 .  Receiving the 201 response

Let’s send an invalid request to see if the validation filter functions properly. Remember that we have added the StringLengthAttribute to the Model property of our Car object, which specifies that this field requires a string value whose length should be a maximum of 20. So now let’s test the logic (see Figure 11-5).

9781430247258_Fig11-05.jpg

Figure 11-5 .  Sending an invalid request using Fiddler

After this POST request is sent, a 400 response should be returned, along with a validation error message (see Figure 11-6).

9781430247258_Fig11-06.jpg

Figure 11-6 .  Receiving the 400 response

As you see, with a little extra implementation you have a nice model validation system. We’ve shown a commonly encountered example here, but action filters come in handy mostly for custom implementations specific to an application.

Authorization Filters

Authorization filters inside the Web API framework allow the authorization of the request just before the action filters and controller action are invoked. An authorization filter needs to implement the IAuthorizationFilter interface. We’ll be working with the AuthorizationFilterAttribute class (see Listing 11-22), which is the default implementation of IAuthorizationFilter, which provides the synchronous OnAuthorization method.

Listing 11-22.  AuthorizationFilterAttribute Abstract Class

public abstract class AuthorizationFilterAttribute :
    FilterAttribute, IAuthorizationFilter {

    public virtual void OnAuthorization(
        HttpActionContext actionContext);
}

As already noted several times, an authorization filter runs before any other filter, and its only method, OnAuthorization, is invoked by passing an instance of HttpActionContext class as a parameter. Inside the OnAuthorization method, the request is supposed to be analyzed to authorize the user according to the authorization logic. If the user cannot be authorized by the authorization logic, the request should be terminated. A new response message should be assigned to indicate the unauthorized request.

You’ll see a sample use case, one where a custom authorization filter is implemented, later in this chapter. Before that, let’s explain the use of the AuthorizeAttribute, which is provided inside the framework out of the box as an authorization filter to work with your custom and framework’s built-in authentication features, including forms and windows authentication.

AuthorizeAttribute and Its Use

Use and logic of AuthorizeAttribute is fairly straightforward in ASP.NET Web API. It checks against the user IPrincipal returned via Thread.CurrentPrincipal. Authorization is denied if one of the following conditions is satisfied:

  • The request is not associated with any user.
  • The user is not authenticated.
  • The user is not in the authorized group of Users.
  • The user is authenticated but is not in the authorized group of Users (if defined) or is not in any of the authorized Roles (if defined).

This filter can be applied globally to secure our entire application, but this attribute can also be set at the controller or action level. Listing 11-23 shows how to register AuthorizeAttribute as a global filter.

Listing 11-23.  AuthorizeAttribute as a Global Filter

protected void Application_Start(object sender, EventArgs e) {

    //Lines omitted for brevity

    GlobalConfiguration.Configuration.Filters.Add(
        new AuthorizeAttribute());
}

When AuthorizeAttribute is registered globally, authorization for every controller action in our application is required, even though you might want to give access to unauthorized users for some part of it. In order to deal with this kind of situation, there is an attribute class, AllowAnonymousAttribute. Actions and controllers marked with this attribute are skipped by AuthorizeAttribute. Listing 11-24 shows a sample use of AllowAnonymousAttribute. In this case, AuthController’s POST action will be skipped by AuthorizeAttribute, and unauthorized users can have access to this endpoint.

Listing 11-24.  Sample Use of AllowAnonymousAttribute

public class AuthController : ApiController {

    //Lines omitted for brevity

    [AllowAnonymous]
    public HttpResponseMessage Post(User user) {
        
        //Lines omitted for brevity
    }
}

In registering AuthorizeAttribute without any properties, access is given to every single user who is authorized in the application, regardless of the user name or role. You can specify authorized roles and users using two public properties of the AuthorizeAttribute class: Roles and Users. These two properties accept a string value. If there are multiple authorized users or roles in an instance, a comma-separated list for the properties can be provided. Listing 11-25 shows an example that gives access only to a user whose user name is Alex or Tugberk.

Listing 11-25.  AuthorizeAttribute and Giving Access Only to Certain Users

[Authorize(Users = "Alex,Tugberk")]
public class CarsController : ApiController {

    //Lines omitted for brevity

    public IEnumerable<Car> Get() {

        //Lines omitted for brevity
    }
}

The same implementation applies to the Roles property, too.

To see the AuthorizeAttribute in action, we have created a small sample application that works with forms authentication. We use only ASP.NET Web API as a server-side component here and deal with communication between the browser and our API using AJAX requests.

image Note  To demonstrate the AuthorizeAttribute use here, we are working with ASP.NET forms authentication, which will work only if an application is hosted under IIS. (Chapter 15 will explain forms authentication.) So don’t be overwhelmed by the sample application. For brevity, the full code for this sample application isn’t provided here, but you can find the full code for the working application with the source code for this book.

The main idea of this application is to allow authorized users to retrieve the cars list inside the car gallery. As we are using forms authentication and the client application is under the same domain as our API, users need to authenticate themselves just once; then the encrypted authentication information will be stored inside the cookie.

Listing 11-26 implements the API controller through which the cars list can be gotten.

Listing 11-26.  CarsController Implementation

public class CarsController : ApiController {

    private readonly CarsContext _carsContext = new CarsContext();

    public IEnumerable<Car> Get() {

        return _carsContext.All;
    }
}

CarsContext is just a class that deals with our data—in-memory data in this case. As we want to give access to the application only to authorized users, we have registered the AuthorizeAttribute as a global filter; the code used to register this filter globally is the same as the code in Listing 11-20.

The other logic needed here is to be able to authorize the users. We will deal with this through a POST request inside AuthController (see Listing 11-27).

Listing 11-27.  AuthController Implementation

public class AuthController : ApiController {

    private readonly AuthorizationService authService =
        new AuthorizationService();

    private readonly FormsAuthenticationService formsAuthService =
        new FormsAuthenticationService();

    [AllowAnonymous]
    public HttpResponseMessage Post(User user) {

        var response = new HttpResponseMessage();

        if (user != null &&
            authService.Authorize(user.UserName, user.Password)) {

            //user has been authorized
            response.StatusCode = HttpStatusCode.OK;
            formsAuthService.SignIn(user.UserName, true);

            return response;
        }

        //if we have come this far, it means that
        //the user hasn't been authorized
        response.StatusCode = HttpStatusCode.Unauthorized;
        response.Content = new StringContent(
            "The user hasn't been authorized.");

        return response;
    }
}

The first thing to mention is the use of the AllowAnonymous attribute. As AuthorizeAttribute is already registered as a global filter, use the AllowAnonymous attribute here to give access to the POST action of AuthController. After all, we are using this action to authorize unauthorized users.

There are two service classes inside our controller to deal with authorization and authentication. Normally, they’d be accessed with dependency injection logic (discussed in Chapter 16), but let’s just use them as they are to stick with the main topic here.

AuthorizationService here controls the authorization by checking the supplied credentials against an in-memory user list (see Listing 11-28). We use the Authorize method of this class to check that the supplied user credentials are valid.

Listing 11-28.  AuthorizationService implementation

public class AuthorizationService {

    private static List<User> users = new List<User>() {
        new User {
            UserName = "tugberk",
            Email = "[email protected]",
            Password = "12345678"
        },
        new User {
            UserName = "alex",
            Email = "[email protected]",
            Password = "87654321"
        }
    };

    public bool Authorize(string userName, string password) {

        var user = users.FirstOrDefault(
            x => x.UserName == userName);

        if (user == null)
            return false;

        return string.Equals(password, user.Password);
    }
}

If the user is successfully authorized by AuthorizationService, the next step is to use FormsAuthenticationService (see Listing 11-29) to deal with ASP.NET forms authentication. The SignIn method of the FormsAuthenticationService is used here. It creates a persistent cookie for the authentication according to information supplied inside web.config, inside the system.web > authentication > forms element. (For more information about the forms element, check this MSDN reference: http://msdn.microsoft.com/en-us/library/1d3t3c61.aspx.

Listing 11-29.  FormsAuthenticationService Implementation

public class FormsAuthenticationService {

    public void SignIn(
        string userName,
        bool createPersistentCookie) {

        if (String.IsNullOrEmpty(userName))
            throw new ArgumentNullException("userName");

        FormsAuthentication.SetAuthCookie(
            userName,
            createPersistentCookie);
    }

    public void SignOut() {
        
        FormsAuthentication.SignOut();
    }
}

If the user can’t be authorized, a 401 unauthorized response gets returned, with a message indicating just that.

When visiting the web page to see the cars list, you should see a log-in dialog box first because you aren’t authorized (see Figure 11-7).

9781430247258_Fig11-07.jpg

Figure 11-7 .  The web page that shows authorized users the cars list

This functionality is enabled with JavaScript code. A request is sent to our API to receive the cars list and show the dialog box if the request is unauthorized (see Listing 11-30).

Listing 11-30.  jQuery Code That Makes an AJAX Request to Our API to Receive the Cars List

//Lines omitted for brevity

var apiBaseAddress = "/api/cars";

//Lines omitted for brevity

$.ajax({
    url: apiBaseAddress,
    type: "GET",
    contentType: "application/json; charset=utf-8",
    statusCode: {
        //OK
        200: function(result) {
        
            $.each(result, function(i, data) {
                vm.cars.push(buidCar(data));
            });
        },
        //Unauthorized
        401: function() {
            
            openAuthDialogBox();
        }
    }
});

//Lines omitted for brevity

Let’s look at the code in Fiddler to see what happens when it runs (see Figure 11-8).

9781430247258_Fig11-08.jpg

Figure 11-8 .  Fiddler view of the unathorized GET request to /api/cars

A 401 response, indicating the request is not authorized, is returned. Now let’s try to log in with the wrong credentials and see the request and response in Fiddler (see Figure 11-9).

9781430247258_Fig11-09.jpg

Figure 11-9 .  A Fiddler view of the unauthorized POST request to /api/auth

image Note  In the example, we sent the credentials as plain text (unencrypted); this is an insecure way of handling authorization. In a real-world application, you should use the Secure Transport Layer (SSL) to provide encryption.

As expected, the server again returns the 401 response. Now let’s try to log in with valid credentials; this time we should get a 200 response, along with a Set-Cookie header (see Figure 11-10).

9781430247258_Fig11-10.jpg

Figure 11-10 .  A Fiddler view of the successful POST request to /api/auth

Now that we’re logged in, the cars list should be visible (see Figure 11-11).

9781430247258_Fig11-11.jpg

Figure 11-11 .  View of the cars list inside the web page

This example shows one way of dealing with forms authentication with ASP.NET Web API. On the other hand, if your Web API application is hosted in a project with an ASP.NET web application (ASP.NET MVC, ASP.NET Web Forms, etc.), you can deal with forms authentication using that web application. You should get the same result regardless of how you authenticate the user with forms authentication.

Require SSL Authorization Filter Sample

You might want to allow requests through HTTPS for your entire Web API application or only certain parts of it. To do this, you can create a custom authorization filter that will reject the request if it doesn’t come through HTTPS. Listing 11-31 implements the RequireHttpsAttribute.

Listing 11-31.  RequireHttpsAttribute

public override void OnAuthorization(
    HttpActionContext actionContext) {

    if (actionContext.Request.RequestUri.Scheme != Uri.UriSchemeHttps) {

        actionContext.Response = new
            HttpResponseMessage(HttpStatusCode.Forbidden) {
                Content = new StringContent("SSL required")
            };
    }
}

Since you’ve seen similar examples demonstrated already, what we’re doing here should be familiar. The only logic here is for checking the schema of the request URI to see whether the request came through HTTPS. If it didn’t, a new response message indicating the 403 Forbidden status code should be set.

Finally, Listing 11-32 shows how to register our filter at the controller level.

Listing 11-32.  Registering RequireHttpsFilter for CarsController

[RequireHttps]
public class CarsController : ApiController {

    public string[] GetCars() {

        return new string[] {
            "Car 1",
            "Car 2",
            "Car 3",
            "Car 4"
        };
    }
}

To demonstrate this properly, we have enabled SSL in IIS Express. If you’d like to enable SSL in your development environment, first open the Properties dialog box by clicking on the project and pressing F4. You should see the “Enable SSL” option (as in Figure 11-12). If it is set to true, your application will have two endpoints, one of them an HTTPS endpoint.

9781430247258_Fig11-12.jpg

Figure 11-12 .  Properties dialog box

Visiting the two endpoints should generate a 403 response for the request made through HTTP (see Figure 11-13) but shouldn’t pose problems for the request made through HTTPS (see Figure 11-14).

9781430247258_Fig11-13.jpg

Figure 11-13 .  403 response for the request made through HTTP

9781430247258_Fig11-14.jpg

Figure 11-14 .  200 response for the request made through HTTPS

This is merely one sample use case for authorization filters. As you’ve seen, creating your own should be pretty easy.

Exception Filters

An exception filter is a special type of filter; it is invoked only when an exception occurs inside an action and its associated filters. An exception filter needs to implement the IExceptionFilter interface. We will be working with the ExceptionFilterAttribute class (see Listing 11-33), which is the default implementation of IExceptionFilter.

Listing 11-33.  ExceptionFilterAttribute Abstract Class

public abstract class ExceptionFilterAttribute :
    FilterAttribute, IExceptionFilter {

    public virtual void OnException(
        HttpActionExecutedContext actionExecutedContext);
}

The OnException method of ExceptionFilterAttribute is executed when an exception occurs and an instance of an HttpActionExecutedContext class (already covered in Table 11-4) is passed as a parameter to this method. This lets us see exception details through the Exception property of HttpActionExecutedContext class and log the exception however we see fit.

To demonstrate use of this filter, Listing 11-34 shows a custom exception filter that writes the exception details to the trace.

Listing 11-34.  HandleErrorAttribute Exception Filter Implementation

public class HandleErrorAttribute : ExceptionFilterAttribute {

    public override void OnException(
        HttpActionExecutedContext actionExecutedContext) {

        var actionCtx = actionExecutedContext.ActionContext;
        var controllerCtx = actionCtx.ControllerContext;

        Trace.TraceInformation(
            "Exception occured. Controller: {0}, action: {1}. Exception message: {2}",
            controllerCtx.ControllerDescriptor.ControllerName,
            actionCtx.ActionDescriptor.ActionName,
            actionExecutedContext.Exception.Message);
    }
}

Listing 11-35 shows how this filter is registered at the controller level.

Listing 11-35.  CarsController with HandleError Exception Filter

[HandleError]
public class CarsController : ApiController {

    public string[] GetCars() {

        throw new Exception();

        return new string[] {
            "Car 1",
            "Car 2",
            "Car 3",
            "Car 4"
        };
    }
}

Notice that we’ve thrown an exception on purpose inside our action method to test its functionality. So we should see the exception details being written to the Output window when the application is run under debug mode (see Figure 11-15).

9781430247258_Fig11-15.jpg

Figure 11-15 .  The Output window showing the result

You can use your favorite logging library to log exceptions (NLog, Elmah, etc.). It’s pretty easy to wire them up with exception filters.

Summary

Filters are one of the richest extensibility points of ASP.NET Web API. You can inject your own logic into specific points to run and handle certain special operations: logging, error handling, authorization, and so on. This chapter has shown how filters behave and how they are implemented; it has also explained different types of filters. Also, to ensure that you can find a place for filters inside your application, sample use scenarios were provided.

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

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