100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

Action Results and Status Codes

How ASP.NET Core action results map to HTTP status codes, and how to choose the right response for every outcome.

Building APIsIntermediate9 min readJul 10, 2026
Analogies

IActionResult and Action Result Types

IActionResult is the base abstraction every action result implements, letting an action return Ok(), NotFound(), BadRequest(), or any other typed outcome from a single method. ActionResult<T> refines this by letting an action declare its typical success payload type while still returning other IActionResult-implementing types like NotFound() from the same method, which also improves the accuracy of generated OpenAPI/Swagger documentation.

🏏

Cricket analogy: IActionResult is like the range of outcomes an umpire can signal after a delivery, out, wide, no-ball, or dot ball, and Ok() or NotFound() are the specific signals a controller action raises, the way a raised finger versus crossed arms communicates a specific ruling to everyone watching.

csharp
[HttpGet("{id:int}")]
public async Task<ActionResult<Order>> GetById(int id)
{
    var order = await _orders.GetByIdAsync(id);
    if (order is null) return NotFound();
    return Ok(order);
}

[HttpPost]
public async Task<ActionResult<Order>> Create(CreateOrderDto dto)
{
    var order = await _orders.CreateAsync(dto);
    return CreatedAtAction(nameof(GetById), new { id = order.Id }, order);
}

[HttpDelete("{id:int}")]
public async Task<IActionResult> Delete(int id)
{
    var deleted = await _orders.DeleteAsync(id);
    if (!deleted) return NotFound();
    return NoContent();
}

Common Status Codes and Their Meaning

Status codes carry precise semantic meaning that clients and infrastructure rely on. 200 OK signals a successful read; 201 Created, paired with CreatedAtAction, signals a new resource was made and includes a Location header pointing to it; 204 No Content signals success with nothing to return, typical for deletes; 400 Bad Request signals malformed input; 404 Not Found signals a missing resource; and 409 Conflict signals the request conflicts with the resource's current state, such as a duplicate.

🏏

Cricket analogy: Returning 201 Created with CreatedAtAction is like a scorer not just confirming a century was scored but also announcing exactly which milestone board to update: the Location header in the 201 response points the client to the new resource's URL, just as the scorer points to precisely where the record now lives.

ASP.NET Core can automatically produce RFC 7807-compliant ProblemDetails responses for error results; BadRequest(ModelState) and NotFound() both serialize into a structured { type, title, status, detail, traceId } payload by default when [ApiController] is applied, giving API clients a consistent error shape instead of ad hoc error JSON.

Content Negotiation

Content negotiation lets the same underlying resource be served in different representations, most commonly JSON by default, and XML if the AddXmlSerializerFormatters() output formatter is registered and the client's Accept header requests application/xml. The server inspects the Accept header and selects a matching output formatter, falling back to the default (JSON) if no acceptable formatter can satisfy the request or if the header is omitted entirely.

🏏

Cricket analogy: Content negotiation is like a stadium's scoreboard offering both a Hindi and English commentary feed and picking based on what the viewer's device requests: the server picks JSON or XML output based on the client's Accept header, the same way the broadcast picks a language feed based on the viewer's selection.

Custom Result Types

For response shapes the built-in helpers don't cover, streaming a generated file, a custom binary format, or a dynamically watermarked image, you can implement a custom ActionResult by overriding ExecuteResultAsync(ActionContext), which gives full control over how bytes are written to the HTTP response. In minimal APIs the equivalent extensibility point is IResult, implemented by writing a class with an ExecuteAsync(HttpContext) method, mirroring the same idea outside the MVC pipeline.

🏏

Cricket analogy: A custom action result implementing ExecuteResultAsync is like a groundskeeper designing a bespoke pitch-covering ritual for a rain delay that isn't in the standard rulebook: most matches use standard umpiring signals (Ok, NotFound), but a custom result lets you define a specialized response format, like streaming a CSV export, that the built-in helpers don't cover.

Returning 200 OK for an operation that actually failed (say, wrapping an error message inside a 200 response body) breaks REST tooling, caching layers, and monitoring that key off HTTP status codes. Always let the status code reflect the actual outcome, 4xx for client errors, 5xx for server errors, even if it feels like extra ceremony for a small internal API.

  • IActionResult and ActionResult<T> let an action return a specific, typed outcome like Ok(), NotFound(), or BadRequest() instead of a bare value.
  • 201 Created should be paired with CreatedAtAction so the response includes a Location header pointing to the new resource.
  • 204 No Content is the correct response for a successful operation, like a delete, that has nothing further to return.
  • 409 Conflict and 404 Not Found represent genuinely different failure conditions and should never be used interchangeably.
  • [ApiController] automatically produces RFC 7807 ProblemDetails responses for validation and error results.
  • Content negotiation lets the same resource be served as JSON or XML depending on the client's Accept header.
  • A custom ActionResult overriding ExecuteResultAsync supports response shapes the built-in helpers don't cover.

Practice what you learned

Was this page helpful?

Topics covered

#ASPNETCoreStudyNotes#MicrosoftTechnologies#ActionResultsAndStatusCodes#Action#Results#Status#Codes#StudyNotes#SkillVeris#ExamPrep