ASP.NET Core
C#
IActionResult
ActionResult
web development

Can't decide between TaskIActionResult, IActionResult and ActionResultThing

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

These return types solve different problems, not competing style preferences. In ASP.NET Core, the real questions are: is the action asynchronous, does it return a strongly typed body, and can it also return non-success results such as NotFound or BadRequest.

The Three Options

Here is the simple breakdown:

  • 'IActionResult means "this action returns an HTTP result"'
  • 'ActionResult<T> means "this action usually returns T, but may also return an HTTP result"'
  • 'Task<...> means "this action is asynchronous"'

So Task<IActionResult> and Task<ActionResult<T>> are just async versions of the same idea.

When IActionResult Fits

Use IActionResult when the action may return different result types and there is no single obvious response body type:

csharp
1[HttpPost]
2public IActionResult Create(OrderDto dto)
3{
4    if (!ModelState.IsValid)
5        return BadRequest(ModelState);
6
7    return RedirectToAction(nameof(GetById), new { id = 123 });
8}

This is a good fit for MVC-style actions or APIs with varied response shapes.

When ActionResult<T> Fits

Use ActionResult<T> when the happy path returns a concrete type, but error paths still need regular HTTP results:

csharp
1[HttpGet("{id}")]
2public ActionResult<ProductDto> GetById(int id)
3{
4    var product = repository.Find(id);
5    if (product is null)
6        return NotFound();
7
8    return product;
9}

This is often the nicest API style because:

  • success responses are strongly typed
  • OpenAPI and tooling can infer the main body type more clearly
  • you still keep the flexibility of NotFound(), BadRequest(), and similar results

For Web API endpoints, this is often the best default when a main response type exists.

When Task<...> Matters

If the action awaits asynchronous I/O such as database calls or HTTP calls, return Task<...>:

csharp
1[HttpGet("{id}")]
2public async Task<ActionResult<ProductDto>> GetById(int id)
3{
4    var product = await repository.FindAsync(id);
5    if (product is null)
6        return NotFound();
7
8    return product;
9}

The Task is not about HTTP semantics. It is about asynchronous execution.

That means:

  • 'Task<IActionResult> is for async actions with varied response shapes'
  • 'Task<ActionResult<T>> is for async actions with a main typed response body'

A Practical Rule of Thumb

Use this default decision tree:

  1. Is the action asynchronous
  2. Does the success case return a specific body type

If yes to both, Task<ActionResult<T>> is usually the best fit.

If async but no obvious success-body type, Task<IActionResult> is fine.

If synchronous and typed, ActionResult<T> is fine.

If synchronous and mixed-result oriented, IActionResult is fine.

What About Returning T Directly

If an action always returns a successful body and never returns alternate HTTP results, returning T directly can be reasonable. But in real APIs, validation failures, missing records, and authorization checks are common, so ActionResult<T> often gives a better balance. It also documents the "normal success body" more clearly to readers of the controller code and generated API descriptions.

Common Pitfalls

The biggest mistake is using Task<IActionResult> everywhere just because the action is async, even when Task<ActionResult<T>> would make the success response clearer.

Another mistake is returning IActionResult for strongly typed APIs and then losing useful metadata for documentation and tooling.

A third issue is making actions async without actually awaiting anything. If the work is purely synchronous, do not add Task just for style.

Summary

  • 'IActionResult is for general HTTP-result flexibility.'
  • 'ActionResult<T> is for typed success responses plus normal HTTP error results.'
  • Wrap either one in Task<...> when the action is asynchronous.
  • 'Task<ActionResult<T>> is often the best default for async Web API endpoints.'
  • Choose the return type based on action semantics, not habit.

Related reading
Course
Intermediate
27 lessons
14 hours
OOD Fundamentals

Master object-oriented design from first principles, SOLID, design patterns, and classic interview problems with hands-on coding.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions