Learn .NET - Web API & HTTP Client
Series/Learn .NET/Episode 11
Episode 11 of 23

Learn .NET - Web API & HTTP Client

This episode opens up the backend world with ASP.NET Core: Web API basics, controllers, routing, model binding, and response formatting, then the concise Minimal APIs, and an HTTP client with resilience patterns for inter-service communication.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

All the previous material leads here: building a Web API. Episode 11 teaches ASP.NET Core — the famously fast .NET web framework — from classic controllers, routing, and model binding, to the Minimal APIs that have become the modern standard.

At the end, you will also use HttpClient to call other APIs with resilience patterns such as retry and timeout. The ability to both call and serve HTTP makes you ready to build distributed systems in phase 5.

ASP.NET Core Web API Basics

Creating a Web API Project

Create a project from the webapi template:

Create a web API project
dotnet new webapi -n Catalog.Api
cd Catalog.Api
dotnet run

dotnet new webapi -n Catalog.Api creates a project with an example weather endpoint. When run, the application uses Kestrel — the .NET web server built directly into the platform. Access http://localhost:5xxx to see the default response.

Endpoint Structure

The modern webapi template uses a Minimal API in Program.cs. This file contains the service registrations and endpoint definitions:

Minimal API Program.cs
var builder = WebApplication.CreateBuilder(args);
 
var app = builder.Build();
 
app.MapGet("/api/health", () => Results.Ok(new { status = "up" }));
 
app.Run();

app.MapGet("/api/health", ...) registers an HTTP GET endpoint. Results.Ok returns a 200 response with a JSON body. Minimal APIs place endpoints in the same place as configuration, keeping small projects concise.

Controllers, Routing, Model Binding

Classic Controllers

For large applications structured per feature, use controllers:

Controller with routing
[ApiController]
[Route("api/produk")]
public class ProdukController : ControllerBase
{
    private readonly AppDbContext _db;
 
    public ProdukController(AppDbContext db)
    {
        _db = db;
    }
 
    [HttpGet("{id:int}")]
    public async Task<ActionResult<Produk>> GetById(int id)
    {
        var produk = await _db.Produk.FindAsync(id);
        return produk is null ? NotFound() : Ok(produk);
    }
}

[ApiController] enables automatic model binding and validation. The api/produk/{id} route maps URLs to methods, and constructor injection (episode 8) connects the controller to the DbContext.

Model Binding and Response Formatting

Model binding translates body, query, and route data into method parameters:

Model binding from body
public record CreateProdukRequest(string Nama, decimal Harga);
 
[HttpPost]
public async Task<ActionResult<Produk>> Create(CreateProdukRequest request)
{
    var produk = new Produk { Nama = request.Nama, Harga = request.Harga };
    _db.Produk.Add(produk);
    await _db.SaveChangesAsync();
    return CreatedAtAction(nameof(GetById), new { id = produk.Id }, produk);
}

The JSON body is automatically parsed into CreateProdukRequest. Responses are converted to JSON with the built-in JsonSerializerOptions — for example, camelCase for consistency with JavaScript consumers.

Minimal APIs

When to Use Minimal APIs

Minimal APIs excel at small projects and prototypes; controllers for large-scale organization. An example Minimal API that handles POST:

Minimal API with DI
app.MapPost("/api/produk", async (CreateProdukRequest req, AppDbContext db) =>
{
    var produk = new Produk { Nama = req.Nama, Harga = req.Harga };
    db.Produk.Add(produk);
    await db.SaveChangesAsync();
    return Results.Created($"/api/produk/{produk.Id}", produk);
});

The CreateProdukRequest and AppDbContext parameters are injected automatically by DI. Results.Created returns 201 with a Location header. This approach removes controller boilerplate while the number of endpoints is still small.

HTTP Client and Resilience Patterns

HttpClient with DI

To call other APIs, register HttpClient in DI. Use a typed client so the configuration is centralized:

Typed HTTP client
builder.Services.AddHttpClient<ICatalogClient, CatalogClient>(client =>
{
    client.BaseAddress = new Uri("https://api.example.com");
    client.Timeout = TimeSpan.FromSeconds(10);
});

AddHttpClient<T> registers a typed client with configuration and a safe singleton lifetime. BaseAddress and Timeout are set once; connection instantiation is managed automatically by IHttpClientFactory — avoiding the socket exhaustion that often happens when using new HttpClient() repeatedly.

Resilience Patterns with Retry

Distributed systems are prone to transient failures. Add retry with Microsoft.Extensions.Http.Resilience:

Install the resilience package
dotnet add package Microsoft.Extensions.Http.Resilience

Then enable retry at registration:

Adding retry
builder.Services.AddHttpClient<ICatalogClient, CatalogClient>()
    .AddStandardResilienceHandler();

.AddStandardResilienceHandler() provides retry, timeout, and circuit breaker with sensible default settings. With this, your service stays healthy even when the upstream API is going through a temporary disturbance.

Tip

Avoid creating new HttpClient() per request. Always use IHttpClientFactory or typed clients from DI to avoid connection leaks and socket exhaustion.

Web API Practice Summary

  • Choose Minimal APIs for small projects; controllers for large structures.
  • Use [ApiController] for automatic model binding and validation.
  • Results.* provides clear HTTP response helpers.
  • Register HttpClient via DI; use typed clients.
  • Enable retry and circuit breaker for inter-service communication.

Closing

Key takeaways:

  • The webapi template produces a Minimal API endpoint that runs right away.
  • Controllers provide per-feature structure with routing and DI.
  • Model binding turns JSON bodies into C# parameters.
  • Minimal APIs offer concise endpoints for small projects.
  • HttpClient via IHttpClientFactory prevents socket exhaustion.
  • The standard resilience handler adds retry and circuit breaker.

In the next episode 12 we will discuss security and identity — ASP.NET Core authentication and authorization, JWT bearer tokens, policy-based authorization, OAuth2 and OpenID Connect integration, and secure headers, CORS, and data protection.