#csharp #dotnet #backend

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 age is missing or less than 0, return a 400 Bad Request.
  • If age is less than 18, return a 403 Forbidden with a message.
  • If age is 18 or older, return a 200 OK welcoming the user.

Tomorrow: Moving back to Controllers (MVC Pattern).