Composing API responses for maximum reuse with ASP.NET Web API

Tags: WebApi, HTTP

In Web API 2.1 a new mechanism was introduced for returning HTTP messages that appeared to be a cross between HttpResponseMessage and the ActionResult mechanism from ASP.NET MVC.  At first I wasn't a fan of it at all.  It appeared to add little new value and just provide yet another alternative that would a be a source of confusion.  It wasn't until I saw an example produced by Brad Wilson that I was convinced that it had value.

The technique introduced by Brad was to enable a chain of action results to be created in the controller action method that would then be processed when the upstream framework decided it needed to have an instance of a HttpResponseMessage.

A composable factory

BuildingBlocks

The IHttpActionResult interface is a single asynchronous method that returns a HttpResponseMessage.  It is effectively a HttpResponseMessage factory.  Being able to chain together different implementations of IHttpActionResult allows you to compose the exact behaviour you want in your controller method with re-usable classes.  With a little bit of extensions method syntax sugar you can create controller actions that look like this,

 public IHttpActionResult Get()
        {
            var home = new HomeDocument();

            home.AddResource(TopicsLinkHelper.CreateLink(Request).WithHints());
            home.AddResource(DaysLinkHelper.CreateLink(Request).WithHints());
            home.AddResource(SessionsLinkHelper.CreateLink(Request).WithHints());
            home.AddResource(SpeakersLinkHelper.CreateLink(Request).WithHints());


            return new OkResult(Request)
                .WithCaching(new CacheControlHeaderValue() { MaxAge = new TimeSpan(0, 0, 60) })
                .WithContent(new HomeContent(home));
        }

The OkResult class implements IHttpActionResult and the WithCaching and WithContent build the a chain with other implementations.  This is similar to the way HttpMessageHandler and DelegatingMessageHandler work together.

Apply your policies

In the example above, the CachingActionResult didn’t add a whole lot of value over just setting the CacheControl header directly. However, I think could be a good place to start defining consistent policies across your API.  Consider writing a bit of infrastructure code to allow you to do,

 public IHttpActionResult Get()
 {
          var image = /* ... Get some image */

            return new OkResult(Request)
                .WithCaching(CacingPolicy.Image)
                .WithImageContent(image);
 }

This example is a bit of a thought experiment, however the important point is, you can create standardized, re-usable classes that apply a certain type of processing to a generated HttpResponseMessage.

Dream a little

If I were to let my imagination run wild, I could see all kinds of uses for this methods,

new CreatedResult(Request)
.WithLocation(Uri uri)
.WithContent(HttpContent content)
.WithEtag()

new ServerNotAvailableResult(Request)
.WithRetry(Timespan timespan)
.WithErrorDetail(exception)

new AcceptResult(Request)
.WithStatusMonitor(Uri monitorUrl)

new OkResult(Request)
 .WithNegotiatedContent(object body)
 .WithLastModified(DateTime lastModified)
 .WithCompression()

Some of these methods might be able to be created as completely general purpose “framework level” methods, but I see them more as being able to apply your API standards to controller responses. 

More testable

One significant advantage of using this approach over using ActionFilter attributes is that when unit testing your controllers, you will be able to test the effect of the ActionResult chain.  With ActionFilter attributes, you need to create an in-memory host to get the full framework infrastructure support in order for the ActionFilter objects to execute.

Another advantage of this approach over action filters for defining action-level behaviour is that by constructing the chain, you have explicit over the order in which the changes are applied to your response. 

More reusable

It may also be possible to take the abstraction one step further and create a factory for pre defined chains.  If the same sets of behaviour are applicable to many different actions then you can create those chains in one place and re-use a factory method.  This can be useful in controllers where there are a variety of different overloads of a method that take different parameters, but the response is always constructed in a similar way.   The method for creating the action result chain can be in the controller itself.

The implementation

The CachingResult class looks like this,

public class CachingResult : IHttpActionResult
    {
        private readonly IHttpActionResult _actionResult;
        private readonly CacheControlHeaderValue _cachingHeaderValue;


        public CachingResult(IHttpActionResult actionResult, CacheControlHeaderValue cachingHeaderValue)
        {
            _actionResult = actionResult;
            _cachingHeaderValue = cachingHeaderValue;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {

            return _actionResult.ExecuteAsync(cancellationToken)
                .ContinueWith(t =>
                {
                    t.Result.Headers.CacheControl = _cachingHeaderValue;
                    return t.Result;
                }, cancellationToken);
        }
    }

and the extension method is simply,

  public static IHttpActionResult WithCaching(this IHttpActionResult actionResult, CacheControlHeaderValue cacheControl)
  {
            return new CachingResult(actionResult, cacheControl);
  }

A more refined implementation might be to create an abstract base class that takes care of the common housekeeping.  It can also take care of actually instantiating a default HttpResponseMessage if the ActionResult is at the end of the chain.  Something like this,

public abstract class BaseChainedResult : IHttpActionResult
    {
        private readonly IHttpActionResult _NextActionResult;

        protected BaseChainedResult(IHttpActionResult actionResult)
        {
            _NextActionResult = actionResult;
        }

        public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
        {
            if (_NextActionResult == null)
            {
                var response = new HttpResponseMessage();
                ApplyAction(response);
                return Task.FromResult(response);
            }
            else
            {

                return _NextActionResult.ExecuteAsync(cancellationToken)
                    .ContinueWith(t =>
                    {
                        var response = t.Result;
                        ApplyAction(response);
                        return response;
                    }, cancellationToken);
            }
        }

        public abstract void ApplyAction(HttpResponseMessage response);
    
    }

A more complete usage example

If you are interested in seeing these classes in use in a complete project you can take a look at the Conference API project on Github.

Image credit: Building blocks https://flic.kr/p/4r9zSK

comments powered by Disqus