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
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()); <span class="kwrd">return</span> <span class="kwrd">new</span> OkResult(Request) .WithCaching(<span class="kwrd">new</span> CacheControlHeaderValue() { MaxAge = <span class="kwrd">new</span> TimeSpan(0, 0, 60) }) .WithContent(<span class="kwrd">new</span> HomeContent(home)); }</pre>
The
OkResult
class implementsIHttpActionResult
and theWithCaching
andWithContent
build the a chain with other implementations. This is similar to the wayHttpMessageHandler
andDelegatingMessageHandler
work together.Apply your policies
In the example above, the
CachingActionResult
didn’t add a whole lot of value over just setting theCacheControl
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 */<span class="kwrd">return</span> <span class="kwrd">new</span> 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 theActionResult
chain. WithActionFilter
attributes, you need to create an in-memory host to get the full framework infrastructure support in order for theActionFilter
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;<span class="kwrd">public</span> CachingResult(IHttpActionResult actionResult, CacheControlHeaderValue cachingHeaderValue) { _actionResult = actionResult; _cachingHeaderValue = cachingHeaderValue; } <span class="kwrd">public</span> Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { <span class="kwrd">return</span> _actionResult.ExecuteAsync(cancellationToken) .ContinueWith(t => { t.Result.Headers.CacheControl = _cachingHeaderValue; <span class="kwrd">return</span> t.Result; }, cancellationToken); } }</pre>
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 theActionResult
is at the end of the chain. Something like this,public abstract class BaseChainedResult : IHttpActionResult { private readonly IHttpActionResult _NextActionResult;<span class="kwrd">protected</span> BaseChainedResult(IHttpActionResult actionResult) { _NextActionResult = actionResult; } <span class="kwrd">public</span> Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { <span class="kwrd">if</span> (_NextActionResult == <span class="kwrd">null</span>) { var response = <span class="kwrd">new</span> HttpResponseMessage(); ApplyAction(response); <span class="kwrd">return</span> Task.FromResult(response); } <span class="kwrd">else</span> { <span class="kwrd">return</span> _NextActionResult.ExecuteAsync(cancellationToken) .ContinueWith(t => { var response = t.Result; ApplyAction(response); <span class="kwrd">return</span> response; }, cancellationToken); } } <span class="kwrd">public</span> <span class="kwrd">abstract</span> <span class="kwrd">void</span> ApplyAction(HttpResponseMessage response); }</pre>
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