Keeping your ASP.NET WebAPI Controllers lean with the use of Filters

In a constant battle to reduce the amount of duplication and technical debt in the code I write, I am always revisiting code and looking at how I can reduce the amount of maintenance work I need to perform. This means that after completing some piece of work, I frequently review the code base to see where patterns are emerging. I also follow the rule of three always and it never seems to fail me.

Since I’ve been doing quite a bit of API development recently, I thought I’d share some improvements which have greatly reduced the amount of boilerplate code I need to look at which keeps me focused on business domain problems.

Lean model validation

One thing I’ve done again and again without realising there was a opportunity for improvement was model validation. I’m sure you’ve used this code again and again.

if (ModelState.IsValid) {

  // Do business stuff

  return Ok();         // HTTP Status 200

} else {
  return BadRequest(); // HTTP Status 401
}

You could encapsulate this logic in a method and reuse it in your controllers by placing the code in a ControllerBase type class, but I feel there’s a much better way. That way is by using a filter.

public class ValidateModelStateAttribute : ActionFilterAttribute
    {       
        public override void OnActionExecuting(ActionExecutingContext context)
        {
            if (!context.ModelState.IsValid)
            {
                context.Result = new BadRequestObjectResult(context.ModelState);
            }

            base.OnActionExecuting(context);
        }
    }

With this filter in place, we simply need to decorate our controller method. This keeps the method small and focused.

[ValidateModelState]
public IActionResult Post() {

  // Do stuff
}

This might seem like a small improvement, but if your working with any sizable WebAPI project which has 10’s or 100’s of public methods, you’re looking at a reduction of a few hundred lines of code or more.

The other great thing about this approach is that you’re returning the appropriate HTTP response for a failure in validation: 400 Bad Request.

Lean Business Layer results

So what if the POST’d view model is valid, but fails further up the stack in the business layer? How can we use a Filter to simplify these repeated responses?

If you think about it, a service call to say, AddOrder() or CreateUser() for example should have two outcomes, it either fails or succeeds. Now there are different reasons for failure, and we can sprinkle a property or two to deal with that, but we have either success or failure.

How I approach this is for all my business services to return a ServiceResponse<T> object.

public class ServiceResponse<T>
{
	public ServiceResponse() : this(default(T)) { }

	public ServiceResponse(T data)
	{
		State = Object.Equals(data, default(T)) ? ServiceResponseState.Failed : ServiceResponseState.Success;
		Data = data;
	}

	public int ResultCode = 0;

	public ServiceResponseState State { get; set; }

	public T Data { get; }

	public string Message { get; set; } = String.Empty;
}

public enum ServiceResponseState
{
	NotAuth = -1,
	Failed = 0,
	Success = 1
}

The ServiceResponse<T> object captures the data I need to pass to the UI layer in the API. It outlines success, failure and also an unauthorised response. You create the response object in your service calls and then perform some basic logic in the controller. We can reduce with a filter but for now I’ll show it’s use.

[HttpPost]
[ValidateModelState]
public IActionResult AddOrder([FromBody] OrderViewModel viewModel)
{
	// Any conversion code required to call the service
	// ...
	
	ServiceResponse<Order> newOrder = orderService.AddOrder(model);
	
	if (newOrder.State == ServiceResponseState.Success) {
		return Ok(newOrder);	
	}
	if (newOrder.State == ServiceResponseState.NotAuth) {
		return Unauthorized()
	} 
	
	return BadRequest(newOrder.Message);
}

You can imagine how this repeated if, else pattern would bloat your controllers over time and it wouldn’t be very DRY either. I want to keep my controllers lean and just focus on the business related code.

To do this, I’m going to create a global filter which determines if what is being returned by a controller is of type ServiceResponse<T>.

public class HttpResponseCodeFilter : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Result is ObjectResult result)
            {
                var genericResponseType = typeof(ServiceResponse<>);

                var resultType = result.Value.GetType();

                if (resultType.IsGenericType && resultType.GetGenericTypeDefinition() == genericResponseType)
                {
                    // The use of dynamic here is to reduce the amount of reflection code I'd need to write
                    dynamic resultValue = result.Value;

                    if (resultValue.State == ServiceResponseState.Success)
                    {
                        context.HttpContext.Response.StatusCode = resultValue.ResultCode == 0 ? 200 : resultValue.ResultCode;
                        context.Result = new OkObjectResult(resultValue.Data);
                    }
                    else if (resultValue.State == ServiceResponseState.NotAuth)
                    {
                        context.HttpContext.Response.StatusCode = resultValue.ResultCode == 0 ? 403 : resultValue.ResultCode; 
                        context.Result = new ForbidResult();
                    }
                    else
                    {
                        context.HttpContext.Response.StatusCode = 400;
                        context.Result = new BadRequestResult();
                        context.Canceled = true;
                    }
                }
            }
            base.OnActionExecuted(context);
        }
    }

Once this filter is registered in my Startup.cs , it will return the correct HTTP status codes for the response and let the client, i.e. a browser, deal with it appropriately instead of the browser checking for a response.status == 1. I’m using HTTP status codes as they were intended.

So if you look at my controller method now, it’s been reduced down to this.

[HttpPost]
[ValidateModelState]
public IActionResult AddOrder([FromBody] OrderViewModel viewModel)
{
	// Any conversion code required to call the service
	// ...
	
	ServiceResponse<Order> newOrder = orderService.AddOrder(model);
	
	return Ok(newOrder);
}

Just two lines of code and you get the correct HTTP status codes returned. In the case of a 200 response, the data is returned without the ServiceResponse<T> being serialized as part of the response.

These two filters have greatly reduced the amount of code in my controllers and is a great example of how filters can give us the ability to extract logic into small reusable bits of code.

Update 5/Mar/18

A developer pointed out to me on Reddit that the use of the dynamic keyword is a code smell and can be avoided via the use of an Interface and having the .Data property of type object on that Interface. Although that approach might work for in the Filter, I require the .Data property to remain strongly typed as it’s used elsewhere throughout the code base.