ASP.NET Core API - ActionResult<T> vs async Task<T>

ASP.NET Core offers the following options for Web API controller action return types:

  • Specific type (T)
  • IActionResult
  • ActionResult<T>

Specific type (T):

The Specific return type is appropriate when you need to return primitive or complex data type without further checking with possibility of different return type (BadRequestResult (400),NotFoundResult (404), andOkObjectResult(200)`.) from the action as follows:

[HttpGet]
public async Task<List<Product>> GetProducts()
{
    return await _repository.GetProductsAsync();

    // Here you can not return Ok(products), NotFound() etc;
    // If you need to return NotFound() etc then use `IActionResult` instead of Specific type.
}

IActionResult type:

The IActionResult return type is appropriate when multiple ActionResult return types are possible in an action as follows:

[HttpGet]
public async Task<IActionResult> GetProductById(int id)
{
    Product product = await _repository.GetProductByIdAsync(id);

    if(product == null)
    {
        return NotFound(); // Here is one return type
    }

    return Ok(product);  // Here is another return type
}

The ActionResult types represent various HTTP status codes. Some common return types falling into this category are BadRequestResult (400), NotFoundResult (404), and OkObjectResult(200).

ActionResult<T> type:

ASP.NET Core 2.1 adds new programming conventions that make it easier to build clean and descriptive web APIs. ActionResult<T> is a new type added to allow an app to return either a response type or any other action result (similar to IActionResult), while still indicating the response type.

ActionResult<T> is more specific to Web APIs in ASP.NET Core >= 2.1 and ActionResult<T> offers the following benefits over the IActionResult type:

  • The [ProducesResponseType] attribute's Type property can be excluded. For example, [ProducesResponseType(200, Type = typeof(Product))] is simplified to [ProducesResponseType(200)]. The action's expected return type is instead inferred from the T in ActionResult<T>.
  • Implicit cast operators support the conversion of both T and ActionResult to ActionResult<T>. T converts to ObjectResult, which means return new ObjectResult(T); is simplified to return T;.

For more details: Controller action return types in ASP.NET Core Web API


Third solution: IActionResult Task, something like this:

[HttpGet]
[ProducesResponseType(typeof(IList<Currency>), 200)]
public async Task<IActionResult> GetAll()
{
    return Ok(await _typeService.GetCurrenciesAsync().ConfigureAwait(false));
}

[HttpGet("{id}", Name = "GetCurrency")]
[ProducesResponseType(typeof(Currency), 200)]
public async Task<IActionResult> Get([FromRoute]int id)
{
    return Ok(await _expenseService.GetCurrencyAsync(id).ConfigureAwait(false));
}

Have a look at an example from Microsoft and why they return the interface instead: IActionResult