ASP.NET Core: Web API Controllers & Minimal APIs
ApiController Pattern
[ApiController]
[Route("api/[controller]")]
[Authorize] // require auth for all endpoints
public class ArticlesController : ControllerBase
{
private readonly IArticleService _service;
public ArticlesController(IArticleService service)
{
_service = service;
}
[HttpGet]
[AllowAnonymous]
[ProducesResponseType<IEnumerable<ArticleDto>>(StatusCodes.Status200OK)]
public async Task<IActionResult> GetAll([FromQuery] ArticleQueryParams query)
{
var articles = await _service.GetAllAsync(query);
return Ok(articles);
}
[HttpGet("{id:int}")]
[ProducesResponseType<ArticleDto>(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(int id)
{
var article = await _service.GetByIdAsync(id);
return article is null ? NotFound() : Ok(article);
}
[HttpPost]
[ProducesResponseType<ArticleDto>(StatusCodes.Status201Created)]
[ProducesResponseType<ValidationProblemDetails>(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Create([FromBody] CreateArticleRequest request)
{
// [ApiController] auto-validates and returns 400 on failure
var article = await _service.CreateAsync(request, User);
return CreatedAtAction(nameof(GetById), new { id = article.Id }, article);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateArticleRequest request)
{
var success = await _service.UpdateAsync(id, request, User);
return success ? NoContent() : NotFound();
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
await _service.DeleteAsync(id);
return NoContent();
}
}Validation
// Data annotations
public class CreateArticleRequest
{
[Required]
[StringLength(200, MinimumLength = 5)]
public string Title { get; set; } = string.Empty;
[Required]
[MinLength(50)]
public string Content { get; set; } = string.Empty;
[Url]
public string? ImageUrl { get; set; }
[Range(1, 10)]
public int Priority { get; set; }
}
// FluentValidation (recommended for complex rules)
// dotnet add package FluentValidation.AspNetCore
public class CreateArticleValidator : AbstractValidator<CreateArticleRequest>
{
public CreateArticleValidator()
{
RuleFor(x => x.Title)
.NotEmpty()
.Length(5, 200)
.Must(title => !title.Contains("spam"))
.WithMessage("Title cannot contain 'spam'");
RuleFor(x => x.Content)
.NotEmpty()
.MinimumLength(50);
RuleFor(x => x.ImageUrl)
.Must(url => Uri.TryCreate(url, UriKind.Absolute, out _))
.When(x => x.ImageUrl != null)
.WithMessage("Invalid URL");
}
}
// Register in Program.cs
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddValidatorsFromAssemblyContaining<CreateArticleValidator>();Minimal APIs (.NET 6+)
// Program.cs — no controllers needed
var app = builder.Build();
var articles = app.MapGroup("/api/articles").RequireAuthorization();
articles.MapGet("/", async (IArticleService svc, [AsParameters] ArticleQueryParams query)
=> await svc.GetAllAsync(query));
articles.MapGet("/{id:int}", async (int id, IArticleService svc) =>
{
var article = await svc.GetByIdAsync(id);
return article is null ? Results.NotFound() : Results.Ok(article);
});
articles.MapPost("/", async (CreateArticleRequest req, IArticleService svc, ClaimsPrincipal user) =>
{
var article = await svc.CreateAsync(req, user);
return Results.CreatedAtRoute("GetArticle", new { id = article.Id }, article);
}).WithName("GetArticle");
articles.MapPut("/{id}", async (int id, UpdateArticleRequest req, IArticleService svc) =>
await svc.UpdateAsync(id, req) ? Results.NoContent() : Results.NotFound());
articles.MapDelete("/{id}", async (int id, IArticleService svc) =>
{
await svc.DeleteAsync(id);
return Results.NoContent();
});
// Typed Results (compile-time checked return types)
articles.MapGet("/{id}", async Task<Results<Ok<ArticleDto>, NotFound>> (int id, IArticleService svc) =>
{
var article = await svc.GetByIdAsync(id);
return article is null ? TypedResults.NotFound() : TypedResults.Ok(article);
});Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free