Aspnet Web Api
title: ASP.NET Core Web API — Complete Guide to Building RESTful APIs description: 'Learn building ASP.NET Core Web APIs: controllers, actions, JSON responses, status codes, model binding, validation, versioning, and OpenAPI/Swagger documentation.' date: 2026-06-28 lastmod: 2026-06-28 weight: 26 tags: [backend, aspnet]
ASP.NET Core Web API provides a framework for building RESTful HTTP APIs with controllers, attribute routing, JSON serialization, model validation, and OpenAPI documentation.
## What You'll Learn
By the end of this tutorial, you'll create Web API controllers, handle CRUD operations, configure JSON serialization, implement API versioning, document with Swagger/OpenAPI, and follow RESTful conventions.
## Real-World Use
A SaaS platform exposes a RESTful API for managing customers, invoices, and payments. Mobile apps and third-party integrations consume the API via JSON over HTTPS.
## Web API Learning Path
```mermaid
flowchart LR
A[JWT] --> B[Web API]
B --> C[Minimal API]
C --> D[SignalR]
D --> E[Testing]
B --> F{You Are Here}
style F fill:#f90,color:#fff
API Controller
[ApiController]
[Route("api/v{version:apiVersion}/products")]
public class ProductsController : ControllerBase
{
private readonly IProductService _service;
public ProductsController(IProductService service) => _service = service;
[HttpGet]
public async Task<ActionResult<IEnumerable<ProductDto>>> GetAll(
[FromQuery] int page = 1, [FromQuery] int size = 10)
{
var products = await _service.GetAllAsync(page, size);
return Ok(products);
}
[HttpGet("{id:int}")]
public async Task<ActionResult<ProductDto>> GetById(int id)
{
var product = await _service.GetByIdAsync(id);
if (product == null) return NotFound();
return Ok(product);
}
[HttpPost]
public async Task<ActionResult<ProductDto>> Create(CreateProductDto dto)
{
var product = await _service.CreateAsync(dto);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
[HttpPut("{id:int}")]
public async Task<IActionResult> Update(int id, UpdateProductDto dto)
{
await _service.UpdateAsync(id, dto);
return NoContent();
}
[HttpDelete("{id:int}")]
public async Task<IActionResult> Delete(int id)
{
await _service.DeleteAsync(id);
return NoContent();
}
}
Swagger/OpenAPI
dotnet add package Swashbuckle.AspNetCore
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My API",
Version = "v1",
Description = "API for managing products"
});
// Add JWT auth to Swagger
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
API Versioning
dotnet add package Microsoft.AspNetCore.Mvc.Versioning
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = new UrlSegmentApiVersionReader();
});
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV1Controller : ControllerBase { ... }
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/products")]
public class ProductsV2Controller : ControllerBase { ... }
Error Handling
// Global exception middleware
app.UseExceptionHandler(handler => handler.Run(async context =>
{
var exception = context.Features.Get<IExceptionHandlerFeature>()?.Error;
var response = new ApiError("Internal server error", 500);
context.Response.StatusCode = 500;
context.Response.ContentType = "application/json";
await context.Response.WriteAsJsonAsync(response);
}));
// ProblemDetails (automatic with [ApiController])
app.UseStatusCodePages();
// Customizes 4xx responses to RFC 7807 ProblemDetails format
Common Mistakes
1. Not Using [ApiController]
Without [ApiController], you lose automatic model validation, binding source inference, and ProblemDetails responses.
2. Returning Domain Models
Domain entities expose internal details. Return DTOs shaped for the API consumer.
3. Ignoring Pagination
Returning all rows without pagination overwhelms clients and databases. Always implement Skip/Take.
4. Not Using ProblemDetails
Standard error format (RFC 7807) helps clients parse errors consistently.
5. Missing API Versioning
Without versioning, breaking changes break existing clients. Version from day one.
Practice Questions
1. What does [ApiController] provide?
Automatic model validation, binding source inference for [FromBody], and ProblemDetails error responses.
2. How do you implement pagination in an API?
Accept page and size query parameters. Use .Skip((page-1)*size).Take(size) in queries.
3. What is the difference between Created and CreatedAtAction?
CreatedAtAction generates a Location header with the URL to retrieve the created resource.
4. How do you version an API?
Use URL segment versioning (/api/v1/products) or header/query parameter versioning.
5. Challenge: Create a complete CRUD API for orders with pagination and Swagger docs.
[ApiController]
[Route("api/v1/orders")]
public class OrdersController : ControllerBase
{
[HttpGet] public async Task<ActionResult<PagedResult<OrderDto>>> GetAll(int page=1, int size=10) { ... }
[HttpGet("{id}")] public async Task<ActionResult<OrderDto>> GetById(int id) { ... }
[HttpPost] public async Task<ActionResult<OrderDto>> Create(CreateOrderDto dto) { ... }
[HttpPut("{id}")] public async Task<IActionResult> Update(int id, UpdateOrderDto dto) { ... }
[HttpDelete("{id}")] public async Task<IActionResult> Delete(int id) { ... }
}
FAQ
{{< faq "Can I return XML from a Web API?" "Yes. Add options => options.FormatterMappings.SetMediaTypeMappingForFormat(\"xml\", \"application/xml\") and request with Accept: application/xml." >}}
Mini Project: Product API
Build a complete REST API for products with Swagger and pagination.
[ApiController]
[Route("api/v1/products")]
public class ProductsController : ControllerBase
{
[HttpGet] public ActionResult<PagedResult<ProductDto>> GetAll(int p=1, int s=10) => Ok(new PagedResult<ProductDto>(items, total, p, s));
[HttpGet("{id}")] public ActionResult<ProductDto> GetById(int id) => Ok(new ProductDto(id, "Laptop", 999.99m));
[HttpPost] public ActionResult<ProductDto> Create(CreateProductDto dto) => CreatedAtAction(nameof(GetById), new { id = 1 }, new ProductDto(1, dto.Name, dto.Price));
}
What's Next
ASP.NET Core Minimal API ASP.NET Core SignalR
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro