Building RESTful Web APIs with ASP.NET Core
ASP.NET Core makes building REST APIs fast and intuitive. In this tutorial, you'll learn how to create a fully functional Web API with proper routing, controllers, and JSON responses — all using .NET's minimal API or controller-based approach.
What is a REST API?
A RESTful API is an architectural style for networked applications that uses HTTP methods (GET, POST, PUT, DELETE) to interact with resources. Resources are represented in JSON format and accessed via URLs called endpoints.
💡 Note: Before starting, make sure you have the .NET 8 SDK installed. Run dotnet --version to verify.
Setting Up Your Project
Start by creating a new Web API project from your terminal:
# Create a new Web API project
dotnet new webapi -n FormatBaseApi
cd FormatBaseApi
dotnet run
Creating Your First Controller
Controllers are C# classes that handle incoming HTTP requests. They inherit from
ControllerBase and use attributes to define routes and actions.
using Microsoft.AspNetCore.Mvc;
namespace FormatBaseApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class TutorialsController : ControllerBase
{
private readonly List<Tutorial> _tutorials = new()
{
new() { Id = 1, Title = "C# Basics", Category = "C#" },
new() { Id = 2, Title = "ASP.NET Core MVC", Category = "ASP.NET" }
};
// GET: api/tutorials
[HttpGet]
public IActionResult GetAll()
{
return Ok(_tutorials);
}
// GET: api/tutorials/1
[HttpGet("{id}")]
public IActionResult GetById(int id)
{
var tutorial = _tutorials.FirstOrDefault(t => t.Id == id);
if (tutorial == null) return NotFound();
return Ok(tutorial);
}
// POST: api/tutorials
[HttpPost]
public IActionResult Create([FromBody] Tutorial tutorial)
{
tutorial.Id = _tutorials.Count + 1;
_tutorials.Add(tutorial);
return CreatedAtAction(nameof(GetById), new { id = tutorial.Id }, tutorial);
}
}
The Tutorial Model
Create a model class to represent the Tutorial resource:
public class Tutorial
{
public int Id { get; set; }
[Required]
public string Title { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public string Level { get; set; } = "Beginner";
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
Routing and URL Structure
The route template api/[controller] uses the controller class name minus the Controller
suffix. For TutorialsController, the base endpoint becomes /api/tutorials.
The action-specific route [HttpGet("{id}")] binds the URL segment to the id parameter.
HTTP Methods Explained
REST APIs use standard HTTP verbs to define what action should be performed on a resource:
- GET — Retrieve one or more resources
- POST — Create a new resource
- PUT — Replace an existing resource completely
- PATCH — Partially update an existing resource
- DELETE — Remove a resource
Model Binding & Validation
ASP.NET Core automatically maps request data into action parameters. Simple types such as int
come from the route or query string, while complex types are bound from the JSON request body.
[HttpPut("{id}")]
public IActionResult Update(int id, [FromBody] Tutorial tutorial)
{
if (id != tutorial.Id)
{
return BadRequest("ID in the route must match the body payload.");
}
var existing = _tutorials.FirstOrDefault(t => t.Id == id);
if (existing == null) return NotFound();
existing.Title = tutorial.Title;
existing.Category = tutorial.Category;
existing.Level = tutorial.Level;
return NoContent();
}
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
var tutorial = _tutorials.FirstOrDefault(t => t.Id == id);
if (tutorial == null) return NotFound();
_tutorials.Remove(tutorial);
return NoContent();
}
Validation attributes such as [Required] and [StringLength] make your model self-validating.
With the [ApiController] attribute, ASP.NET Core automatically returns a 400 Bad Request
response when the request body is missing required fields.
Dependency Injection
ASP.NET Core has built-in dependency injection for services and repositories. Use interfaces to keep controllers testable and separate business logic from HTTP handling.
public interface ITutorialRepository
{
IEnumerable<Tutorial> GetAll();
Tutorial? GetById(int id);
Tutorial Create(Tutorial tutorial);
void Update(Tutorial tutorial);
void Delete(int id);
}
public class TutorialRepository : ITutorialRepository
{
private readonly List<Tutorial> _tutorials = new()
{
new() { Id = 1, Title = "C# Basics", Category = "C#" },
new() { Id = 2, Title = "ASP.NET Core MVC", Category = "ASP.NET" }
};
public IEnumerable<Tutorial> GetAll() => _tutorials;
public Tutorial? GetById(int id) => _tutorials.FirstOrDefault(t => t.Id == id);
public Tutorial Create(Tutorial tutorial)
{
tutorial.Id = _tutorials.Count + 1;
_tutorials.Add(tutorial);
return tutorial;
}
public void Update(Tutorial tutorial)
{
var existing = _tutorials.First(t => t.Id == tutorial.Id);
existing.Title = tutorial.Title;
existing.Category = tutorial.Category;
existing.Level = tutorial.Level;
}
public void Delete(int id) => _tutorials.RemoveAll(t => t.Id == id);
}
// In Program.cs
builder.Services.AddSingleton<ITutorialRepository, TutorialRepository>();
Inject the repository into the controller using constructor injection. This keeps the controller logic clean and focused on HTTP behavior.
Error Handling & Status Codes
A good REST API returns clear status codes and error details. Use the built-in helper methods on ControllerBase
to return the correct response type for each scenario.
if (tutorial == null)
{
return NotFound();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return CreatedAtAction(nameof(GetById), new { id = tutorial.Id }, tutorial);
For API clients, use 200 OK for successful reads, 201 Created for new resources, 204 No Content for successful updates/deletes, and 404 Not Found when resources are missing.
Testing with Swagger
ASP.NET Core automatically integrates Swagger UI when you create a Web API project. Navigate to
https://localhost:PORT/swagger to test your endpoints interactively without needing Postman.
Swagger shows your available endpoints, request schemas, and response examples. It is an essential tool for building and sharing APIs.
✅ Next Steps: In the next tutorial, we'll add Entity Framework Core for database persistence and implement full CRUD with SQL Server.
Summary
REST APIs use HTTP methods and routes to manage resources in a predictable way.
Controllers and route attributes define accessible endpoints and how data is passed into actions.
ASP.NET Core model binding and validation make it easy to accept JSON payloads and enforce request rules.
Dependency injection keeps API services modular and testable, while status codes make API behavior clear.
Swagger gives you an interactive way to document and test your API endpoints.