In Adding Web API Support to an Existing ASP.NET MVC Project we saw the basics of how to create Web API controllers.

In this post we will see how we can add Action Filters to a Web API Controller.

For starters we will create a simple Web API controller to use as an example. Our controller will simply return a list of persons.

Lets start by creating a class representing the Person concept:

public class Person
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
    }

We will also create a dummy repository that returns a list of Persons.
The interface:

using System.Collections.Generic;
using TestWebAPI_1.Models;

namespace TestWebAPI_1.Contracts
{
    public interface IPersonRepository
    {
        List GetAll();
    }
}

The implementation:

using System.Web;
using TestWebAPI_1.Contracts;
using TestWebAPI_1.Models;

namespace TestWebAPI_1.Repository
{
    public class PersonRepository: IPersonRepository
    {
        public List GetAll()
        {
            List list = new List();

            string[] items = { "John", "Henry", "Jane", "Martha" };

            foreach (string item in items)
            {
                Person currentPerson = new Person
                {
                    Name = item,
                    Id = Guid.NewGuid()
                };
                list.Add(currentPerson);
            }
            return list;
        }
    }
}

Finally we create our controller:

using TestWebAPI_1.Models;
using TestWebAPI_1.Repository;
using TestWebAPI_1.Contracts;
using TestWebAPI_1.Helpers.Filters;

namespace TestWebAPI_1.Controllers
{
    [RoutePrefix("api/Person")]
    public class PersonController : ApiController
    {
        IPersonRepository repository = new PersonRepository();

        [HttpPost]
        [Route("GetAll")]
        public List GetAll()
        {
            return repository.GetAll();
        }
    }
}

If we do now a post call using, for example, Postman we will obtain something like this:

[
    {
        "Id": "41f89e13-84c5-44c2-b8ba-5242e7c8276a",
        "Name": "John"
    },
    {
        "Id": "8cdcd5a8-eae0-4ed5-be98-039180156f60",
        "Name": "Henry"
    },
    {
        "Id": "6fe13b9d-75de-48ca-88a0-1ced33b4b7e6",
        "Name": "Jane"
    },
    {
        "Id": "b5d72772-b170-43d7-81bc-25a8ed67b5e4",
        "Name": "Martha"
    }
]

So far so good: we have our basic WebAPI controller up and running.

Adding an Action Filter to our Web API Controller

Now lets imagine that we need the client to send a determined set of data every time it makes an API call.

Let´s create an object to encapsulate the information the client sends us when he does an API call. For example, let´s suppose that we have various types of web clients consuming our API, and that each of them can be identified by an ID. We will also send as an extra data a string code.

Model Data:

namespace TestWebAPI_1.Models
{
    public class ModelData
    {
        public string id { get; set; }
        public string code { get; set; }
    }
}

The client sends that data to the server, and the server does receive it and does something with it: for example log it for usage statistics, or returning the information according a set of preferences of the client, etc. Anyway, we have decided that the clients must compulsory send that data: if that is not the case we want the server to throw an error.

Now let´s write the Action Filter. In our very simple Action Filter we must focus on the following:

  • An Action Filter inherits form the class ActionFilterAttribute
  • We have various functions we can override. Let´s focus in the OnActionExecuting method: this method executes logic before the action executes.

Action Filter:

using System;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using TestWebAPI_1.Models;

namespace TestWebAPI_1.Helpers.Filters
{
    public class MyCustomAttribute : ActionFilterAttribute
    {
        string parameter="keyData";
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            if (actionContext.ActionArguments == null || !actionContext.ActionArguments.ContainsKey(parameter))
                throw new Exception(string.Format("Parameter '{0}' not present", parameter));

            ModelData model = actionContext.ActionArguments[parameter] as ModelData;

            if (String.IsNullOrEmpty(model.id) || String.IsNullOrEmpty(model.code))
                throw new Exception("Error: could not find key data");
        }
    }
}

In the code above we have placed logic in the OnActionExecuting to check the parameters we have received from the client, and check that we have both an id parameter and a codeparameter.

Moreover we check that both those parameters have a value. Otherwise we raise an error.

Now we can modify our web API controller to add the Action Filter we have just created.An Action Filter can modify a whole class: in that case all the methods in the API Controller are affected. Or we can set it to affect only a determined class.

In our sample we have only a function in our controller, so let´s just place it so that it affects the whole class.

using TestWebAPI_1.Models;
using TestWebAPI_1.Repository;
using TestWebAPI_1.Contracts;
using TestWebAPI_1.Helpers.Filters;

namespace TestWebAPI_1.Controllers
{

    [RoutePrefix("api/Person")]
    [MyCustomAttribute]
    public class PersonController : ApiController
    {
        public IPersonRepository repository = new PersonRepository();

        [HttpPost]
        [Route("GetAll")]
        public List GetAll(ModelData keyData)
        {
            return repository.GetAll();
        }
    }
}

Now every time that GetAll is called, before the code in GetAll is executed (remember that OnActionExecuting is executed before the logic in the action) the code in our filter that checks the presence of the id and code parameters is fired. If they are found then it proceeds to the normal execution of GetAll, otherwise an error will be thrown and the code in GetAll will not run.

Thanks for reading. If you found this post useful you can subscribe to my blog to be notified of new posts.

Advertisements