Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly implement asynchronicity in web api 2

I've been seeing some conflicting information on the subject, and I would like to achieve some clarity here.

Originally, you would have Web Api actions such as:

  • Model Action();
  • HttpResponseMessage Action();

Then, with the addition of TPL enhancements to Web Api, you could have

  • Task<Model> Action();
  • Task<HttpResponseMessage> Action();

And supposedly, ASP.NET would be smart enough to handle those by waiting for the task to finish, and by releasing the thread that picked up the request in the mean time.

Beautiful! Everything was simple in the world. But now, with Web API 2, there is a the addition of IHttpActionResult, a third case, which provides the Task<HttpResponseMessage> ExecuteAsync() method.

First off, if you inspect implementations of IHttpActionResult, you see that every ExecuteAsync actually just returns Task.FromResult (not async at all, and does not seem like it would give any performance gain over synchronously returning HttpResponseMessage). Second, I see people recommending to use Task<IHttpActionResult>, which seems entirely redundant. I even see a handful of people suggesting that you combine these approaches and return Task.Factory.StartNew(..) from your action, which seems kind of absurd.

So what is the correct way to use IHttpActionResult asynchronously? Do you simply implement your own, and do asynchronous operations there†, or do you return Task<IHttpActionResult>, make your method async, and await on IO-bound operations before calling to return Ok() (or equivalent)?

To be clear, I do obviously understand that simply going from IHttpActionResult Action(); to Task<IHttpActionResult> Action(); without changing the method body will not help in any way. It is the intent of the ExecuteAsync that is a puzzle.

† https://stackoverflow.com/a/21609402/661422

like image 831
Kir Avatar asked Oct 20 '22 11:10

Kir


1 Answers

Everything was simple in the world. But now, with Web API 2, there is a the addition of IHttpActionResult

IHttpActionResult came to the world for the purpose of encapsulating the generation of HttpResponseMessage. This would let you encapsulate common logic for creating similar responses inside a set of classes which could be reused, easily tested, and would free the controller actions to take care of actual business logic.

First off, if you inspect implementations of IHttpActionResult, you see that every ExecuteAsync actually just returns Task.FromResult

I'm assuming that happens because, for example, NotFound(); and Ok() extension methods simply return a constructed HttpResponseMessage, nothing more. Perhaps you haven't ran into real world scenarios where that would be needed.

Second, I see people recommending to use Task, which seems entirely redundant.

You would return a Task<IHttpActionResult> if your action uses a naturally asynchronous operation. I don't see how that can be redundant. It is needed.

So what is the correct way to use IHttpActionResult asynchronously?

The correct way is the same as for all async methods. Lets say you want to log every response before returning to a 3rd party logging framework, which exposes an asynchronous API via HTTP endpoint, you'd do:

public class LogActionResult : IHttpActionResult
{
   string uri = /* ... */

   public async Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
   {
       var response = new HttpResponseMessage()
       {
           Content = new StringContent(_value),
           RequestMessage = _request
       };

       var httpClient = new HttpClient();
       await httpClient.PostAsJsonAsync(uri, response);

       return response;
   }
}

Asynchronoysly log and return the HttpResponseMessage. If you have no async operation, Task.FromResult would be perfectly fine.

like image 186
Yuval Itzchakov Avatar answered Nov 01 '22 12:11

Yuval Itzchakov