With ASP.NET Core Web API, a controller class is used to handle HTTP requests. A controller class will use Route information to figure out which method to use to handle a particular request. The return type of the method can be a specific type, an IActionResult or an ActionResult.
All these types can be wrapped into a Task or async Task. The question here is that how do you decide which return type to use for each method?
Specific type can be a primitive type (int) or complex data type (custom object). It will be sufficient when the method is only required to a specific type or a null. It wouldn’t be a good idea to return a specific type when exception are handled. For example, returning a StatusCode “not found”.
In this case, IActionResult type can be used. IActionResult is a interface, when IActionResult is used as return type, you can return all the objects that implement IActionResult . The abstract class ActionResult implements IActionResult. There are a lot of build-in status code function returning IActionResult, such as NotFound(), Accepted(), OK() and so on. You can even wrap specific type into OkObjectResult. return OK(product);
From ASP.NET Core2.1, ActionResult can be used as returned type. Instead of
return OK(product);
A easier way is introduced.
return product;
If you don’t need to use await, don’t use await and neither async. If await is used, the return type must be void or Task.
When do you need to use Task<> return type? When the controller is just pass through an Task return type. For example:
[HttpGet] public Task<IEnumerable<OrganizationEntity>> Get() { return GetOrganizationsInternal(); }
When do you need to use async Task<> return type? When you need to wait a async task. For example:
[HttpGet] public async Task<UserPermission> Permissions() { var user = await _userRepository.GetUserByName( Request.HttpContext.User.Identity.Name); if (user != null) { // Do something return GetPermissions(user); } .... }
In this example, user info is needed before getting user permissions. So await is used to get user result. To use await, method must be a async method. The method GetPermissions(user) will return user’s permission, which is not a Task type, it’s a UserPermission type. Because this is a async method, the UserPermission type will be wrapped into a Task automatically. Remember, if it’s a async method, your returned object is a specific type, not a Task.
In Web API, each HTTP request is served by a thread from the thread pool. The controller methods are already called by different thread, what’s the reason to make the return type Task or async Task?
Before answering this question, we need to understand how threads in the thread pool works. Keep in mind, the number of threads in the thread pool is limited. Let’s assume that we have only two threads in the thread pool. If there are two time-consuming requests are served by these two thread, the 3rd requests will be sit in the queue and resulting a timeout from the client side. By using an async Task, after the thread is await a time-consuming async call in the method. The thread is pulled back to the thread pool and can be used to serve other requests. When the time-consuming async call finished, a thread will be allocate back to the awaiting position to continue executing. This way extends the scalability of the server. Note that the time-consuming task should not be a CPU intensive task, otherwise, it may not help that much. A time-consuming task typically a download from a slow network doesn’t need much CPU time should be made as a async call to free up threads from thread pool.
Also, a async Task can speedup the execution when a call is depends on multiple time-consuming tasks. For example, a method needs to download 3 files to process the output, then 3 async download methods are called. This 3 files can be downloaded in the same time, during awaiting this 3 results, the method thread can process other code in this method or return back to the thread pool to server other requests. Without the async keywords, you can’t use await to wait the downloading files, you have to download the file one by one. Note that you can still use .Result() method to get the result of async method, await (WaitAll and etc.) is not the only way to get the result. But await is the only way you can wait for multi async methods. This is a big difference between await and .Result(), check the following example.
[HttpGet] public async Task DownloadAll() { //The following code will only take the longest time of d1, d2 and d3. var d1 = download1(); var d2 = download2(); var d3 = download3(); var d1Result = await d1; var d2Result = await d2; var d3Result = await d3; //The following code will take the the sum of time taken by d1, d2 and d3 d1Result = download1().Result(); d2Result = download2().Result(); d3Result = download3().Result(); ... }
Reference:
docs.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-2.1
stackoverflow.com/questions/37748470/web-api-controller-and-thread-pool