ASP.NET Core Web API
Priority: P1 (OPERATIONAL)
Modern ASP.NET Core patterns for building RESTful APIs.
Implementation Guidelines
- •Minimal APIs: Use for simple endpoints.
app.MapGet(), route groups, endpoint filters. - •Controllers: Use
[ApiController]for automatic model validation and binding. - •Middleware: Order matters. Custom middleware with
app.Use(). - •Filters: Action filters for cross-cutting concerns. Exception filters for error handling.
- •Validation: FluentValidation or DataAnnotations. Return
ProblemDetailson errors. - •Versioning: URL-based (
/api/v1/) or header-based versioning. - •OpenAPI: Always configure Swagger for API documentation.
- •Response Types: Use
TypedResultsfor compile-time safety.
Anti-Patterns
- •No
HttpClientwithoutIHttpClientFactory: Socket exhaustion risk. - •No blocking async: Never
.Resultor.Wait()in controllers. - •No business logic in controllers: Controllers should only orchestrate.
- •No returning
Taskwithoutasync: Useasynckeyword or return directly.
Code
csharp
// Minimal API with route groups
var app = WebApplication.CreateBuilder(args).Build();
var users = app.MapGroup("/api/users")
.WithTags("Users")
.RequireAuthorization();
users.MapGet("/", async (IUserService service) =>
TypedResults.Ok(await service.GetAllAsync()));
users.MapGet("/{id:int}", async (int id, IUserService service) =>
await service.GetByIdAsync(id) is { } user
? TypedResults.Ok(user)
: TypedResults.NotFound());
users.MapPost("/", async (CreateUserDto dto, IUserService service) =>
{
var user = await service.CreateAsync(dto);
return TypedResults.Created($"/api/users/{user.Id}", user);
}).AddEndpointFilter<ValidationFilter<CreateUserDto>>();
// Controller with proper patterns
[ApiController]
[Route("api/[controller]")]
[Produces("application/json")]
public class OrdersController(IOrderService orderService) : ControllerBase
{
[HttpGet("{id:int}")]
[ProducesResponseType<OrderDto>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetOrder(int id, CancellationToken ct)
{
var order = await orderService.GetByIdAsync(id, ct);
return order is null ? NotFound() : Ok(order);
}
[HttpPost]
[ProducesResponseType<OrderDto>(StatusCodes.Status201Created)]
[ProducesResponseType<ValidationProblemDetails>(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateOrder(CreateOrderDto dto, CancellationToken ct)
{
var order = await orderService.CreateAsync(dto, ct);
return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
}
}
Reference & Examples
For middleware, exception handling, and HttpClientFactory: See references/REFERENCE.md.
Related Topics
security | razor-pages | blazor