CodeOath
← All posts
.NET Core / Web API70 min total · 19 parts

Building REST APIs with ASP.NET Core: Routing, Middleware, and Dependency Injection

Contents — Part 8 of 19: Action Results and Status Codes
Part 8 of 19 · ~1 min

Action Results and Status Codes

An action method typically returns IActionResult, ActionResult<T>, or a bare value — each with different implications:

[HttpGet("{id}")]
public async Task<ActionResult<Order>> GetById(int id)
{
    var order = await _orderService.GetByIdAsync(id);
    if (order is null) return NotFound();       // 404, no body
    return Ok(order);                            // 200, JSON body
}

ActionResult<T> lets a single method return either a concrete T (implicitly wrapped as a 200 with that body) or any IActionResult (like NotFound()) for the non-happy paths — cleaner than committing to one or the other across every branch.

HelperStatusTypical use
Ok(value)200Successful GET/PUT with a body to return
CreatedAtAction(...)201Successful POST — includes a Location header pointing at the new resource
NoContent()204Successful action with nothing meaningful to return (a DELETE, or a PUT that doesn't echo the resource back)
BadRequest(...)400The request itself is malformed or fails validation
Unauthorized()401No valid credentials were presented at all
Forbid()403Valid credentials, but insufficient permission for this action
NotFound()404The requested resource doesn't exist
Conflict(...)409The request conflicts with the current state of the resource (e.g. a duplicate key)

CreatedAtAction(nameof(GetById), new { id = order.Id }, order) is worth understanding precisely: it generates the Location header by resolving the URL for the named action (GetById) with the given route values, rather than hardcoding a URL string — so it stays correct even if the route template for GetById later changes.