Things you didn’t know about action return types in ASP.NET Web API

When you are developing an ASP.NET Web API application, you can chose whether you want to return a POCO from your action, which can be any type that will then be serialized, an instance of HttpResponseMessage, or, since Web API 2, an instance of IHttpActionResult.

Let’s have a look at what really happens under the hood afterwards, and discuss some of the things about the response pipeline that you might have not known before.

Understanding the role of IHttpActionInvoker

Once a suitable action is selected to handle an HTTP request, a service called IHttpActionInvoker (or, typically, its default implementation, ApiControllerActionInvoker) is responsible for producing an HttpResponseMessage out of your action.

The service is replaceable and you can provide a custom implementation yourself. The signature is very simple:

In other words, the invoker gets an instance of HttpActionContext and is expected to produce an HttpResponseMessage out of it. This is done by invoking an ExecuteAsync method on the HttpActionDescriptor present on the HttpActionContext. The result of this operation is the object returned by your action (a method on a controller).

Upon obtaining the object returned by your action, the service can do 3 things:

– if you return a POCO, run one of the built-in IActionResultConverter. This effectively means that it will call Request.CreateResponse and run content negotiation on the response, and create an instance of HttpResponseMessage with an ObjectContent<T>. If the return is void, an empty HttpResponseMessage, with status code 204 is created instead.

– if you return IHttpActionResult, it will execute it. This ends up producing an instance of HttpResponseMessage too.

– if you have already produced an HttpResponseMessage yourself in the action, the service will do nothing, as there is no need to construct a new instance of the response anymore

Mixing different action return types

It is a fairly common question – how can I mix, say, POCO return types with HttpResponseMessage, in the same action?

The most common way you’d see being suggested in different Web API articles and publications is to use HttpResponseException. For example, consider the following action:

The special handling of exceptions of HttpResponseException type allow you to count on the framework to use the instance of HttpResponseMessage supplied in the exception’s constructor.

Of course, some would argue that using exceptions to control the flow of your application code is not the best practice. Turns out, the same logical result can be achieved by tweaking the action accordingly:

Using object as return type is obviously going to allow you to return anything from the action (the code will compile). Now, the reason why this is going to work in the Web API pipeline, is that ApiControllerActionInvoker is always inspecting the return object at runtime anyway, and will pick the correct behavior regardless of whether your code path returned a POCO, HttpResponseMessage or IHttpActionResult.

Find out the runtime return type of an action

Suppose you are in an action filter, and want to perform some activity after the action has executed based on the runtime return type of the action.

Here is an example:

Now, imagine a filter:

Normally, you can reach into the HttpActionDescriptor, which stores the return type information.

However, this is obtained statically, from the MethodInfo of the action, not at runtime!

Which means, if you apply it to our example, even if you return an instance of VipPerson, the action descriptor ReturnType property will tell you its Person. Also, by that time (inside a filter), the real instance of an object returned from the action is no longer available, as its already been converted into the HttpResponseMessage.

It would be elegant to implement a custom IHttpActionInvoker which will store the real runtime type of the response object in the request properties disctionary (so that it’s available for later use anywhere).

This is shown below. Notice that as soon as the result of the action is obtained, we store the runtime type in the request Properties, under RuntimeReturnType key. The rest of the code is simply mimicking what ApiControllerActionInvoker does – the code has to be internalized, since its not overrideable in any way).

Only thing left is to register the invoker against your HttpConfiguration:

You can now know exactly what the action has returned – in any place where you have access to the HttpRequestMessage (so de facto anywhere in the Web API pipeline) – by just checking the RuntimeReturnType key of the request’s properties.

Share the post!

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1693()

  • Koen Metsu

    How does the IHttpActionResult work with testing the controller in isolation?
    Let’s say I want to add some headers to the response message in the ExecuteAsync method of a subclass of IHttpActionResult (like in your other post about IHttpActionResults).
    When testing with controller.Get(…), the ExecuteAsync method does not get called, resulting in no added headers in the unit test.

    What’s the proper way to handle this scenario?