Day 14: Returning Data & Status Codes
Welcome to Day 14. Yesterday we returned plain strings and objects. While ASP.NET Core automatically converts those objects to JSON and returns a 200 OK, sometimes we need more control.
What if a user requests /users/999 but user 999 doesnât exist? Returning a blank 200 OK is terrible API design. We should return a 404 Not Found.
Enter IResult
To have full control over the HTTP Response, your Minimal API endpoints should return IResult.
The static Results class provides helper methods to generate these responses easily.
app.MapGet("/products/{id}", (int id) =>
{
var product = db.Products.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return Results.NotFound(new { Message = "Product not found." }); // 404
}
return Results.Ok(product); // 200 OK
});
Common Status Codes in APIs
Here forms the backbone of RESTful communication:
- 200 OK: Success (
Results.Ok(data)) - 201 Created: Successful POST request (
Results.Created($"/url/{newId}", newData)) - 204 No Content: Successful DELETE or PUT, but nothing to return (
Results.NoContent()) - 400 Bad Request: The client sent bad data (
Results.BadRequest("Invalid Email")) - 401 Unauthorized: The client isnât logged in (
Results.Unauthorized()) - 403 Forbidden: The client is logged in, but lacks permissions (
Results.Forbid()) - 404 Not Found: The resource doesnât exist (
Results.NotFound()) - 500 Internal Server Error: Your code crashed (
Results.Problem())
Problem Details (Standardized Errors)
Instead of returning random strings when something goes wrong, modern APIs use RFC 7807 Problem Details. ASP.NET Core natively supports this!
app.MapPost("/checkout", (Cart cart) =>
{
if (cart.Items.Count == 0)
{
// Generates a standardized, machine-readable JSON error object
return Results.Problem(
title: "Cart is empty",
detail: "You must add items to the cart before checking out.",
statusCode: StatusCodes.Status400BadRequest
);
}
return Results.Ok();
});
TypedResults (Advanced)
In .NET 7+, Microsoft introduced TypedResults. It works exactly like Results, but it improves performance slightly and makes Unit Testing Minimal APIs much easier because it preserves the exact C# type returned.
app.MapGet("/hello", () => TypedResults.Ok("Hello Typed World"));
Challenge for Day 14
Create a Minimal API endpoint that accepts an age parameter in the query string.
- If
ageis missing or less than 0, return a400 Bad Request. - If
ageis less than 18, return a403 Forbiddenwith a message. - If
ageis 18 or older, return a200 OKwelcoming the user.
Tomorrow: Moving back to Controllers (MVC Pattern).