.NET track

ASP.NET Core Web API Tutorial

Modern REST APIs with controllers, EF Core, JWT, OpenAPI, versioning and rate limiting.

IntermediateExamples target .NET 9.
0/37 complete
Topics
37
Full lessons
37
Est. hours
~9
Interview Qs
27

Overview

ASP.NET Core Web API is the standard way to build HTTP APIs on modern .NET. You can use controllers or minimal APIs, both backed by the same middleware pipeline and dependency injection.

This track builds a complete, secure, documented CRUD API and connects it to React and Angular front ends.

What you will learn

  • Design RESTful endpoints with correct verbs and status codes
  • Validate input with DTOs and data annotations
  • Persist data with EF Core or ADO.NET
  • Secure APIs with JWT, CORS and rate limiting

Prerequisites

You will get the most from this track if you already know:

Learning path

The recommended sequence, from beginner to advanced. Each stage builds on the one before.

  1. 1REST Foundations · 4 topics · Beginner
    Introduction to Web APIs · REST Principles · HTTP Methods · HTTP Status Codes
  2. 2Building APIs · 7 topics · Beginner → Intermediate
    Controllers and Endpoints · Routing · Request and Response Models · DTOs · Model Validation · Dependency Injection · Service Layer
  3. 3Data · 8 topics · Intermediate
    ADO.NET Integration · Entity Framework Core · SQL Server · Stored Procedures · CRUD API · Pagination · Filtering and Sorting · Searching
  4. 4Cross-Cutting · 9 topics · Intermediate → Advanced
    Global Exception Handling · Logging · Swagger and OpenAPI · JWT Authentication · Role-Based Authorization · CORS · API Versioning · Rate Limiting · File Upload APIs
  5. 5Quality & Delivery · 9 topics · Advanced
    API Testing · Unit Testing · Integration Testing · Performance · Secure API Design · Docker · Azure Deployment · React + ASP.NET Core API Project · Angular + ASP.NET Core API Project

Complete topic list

37 topics
1

REST Foundations

4 topics
  1. Introduction to Web APIsBeginner6m
  2. REST PrinciplesBeginner7m
  3. HTTP MethodsBeginner8m
  4. HTTP Status CodesBeginner7m
2

Building APIs

7 topics
  1. Controllers and EndpointsBeginner6m
  2. RoutingBeginner6m
  3. Request and Response ModelsBeginner7m
  4. DTOsBeginner8m
  5. Model ValidationBeginner7m
  6. Dependency InjectionIntermediate6m
  7. Service LayerIntermediate7m
3

Data

8 topics
  1. ADO.NET IntegrationIntermediate9m
  2. Entity Framework CoreIntermediate8m
  3. SQL ServerIntermediate7m
  4. Stored ProceduresIntermediate8m
  5. CRUD APIIntermediate7m
  6. PaginationIntermediate8m
  7. Filtering and SortingIntermediate7m
  8. SearchingIntermediate7m
4

Cross-Cutting

9 topics
  1. Global Exception HandlingIntermediate7m
  2. LoggingIntermediate6m
  3. Swagger and OpenAPIIntermediate6m
  4. JWT AuthenticationIntermediate7m
  5. Role-Based AuthorizationIntermediate7m
  6. CORSIntermediate6m
  7. API VersioningIntermediate6m
  8. Rate LimitingAdvanced7m
  9. File Upload APIsAdvanced7m
5

Quality & Delivery

9 topics
  1. API TestingAdvanced7m
  2. Unit TestingAdvanced7m
  3. Integration TestingAdvanced7m
  4. PerformanceAdvanced7m
  5. Secure API DesignAdvanced8m
  6. DockerAdvanced5m
  7. Azure DeploymentAdvanced6m
  8. React + ASP.NET Core API ProjectAdvanced8m
  9. Angular + ASP.NET Core API ProjectAdvanced8m

Code example

A taste of the track, from Introduction to Web APIs.

C#Program.cs Illustrative
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();                                  // keep the demo output clean
builder.Services.AddControllers();                                 // controller-based APIs


var app = builder.Build();
app.MapControllers();
app.MapGet("/api/status", () => new { status = "ok", version = "1.0" });   // a minimal API endpoint


app.Urls.Add("http://127.0.0.1:5201");
await app.StartAsync();


using var http = new HttpClient { BaseAddress = new Uri("http://127.0.0.1:5201") };
foreach (var path in new[] { "/api/products", "/api/products/2", "/api/products/9", "/api/status" })
{
    var response = await http.GetAsync(path);
    var type = response.Content.Headers.ContentType?.MediaType ?? "-";
    Console.WriteLine(
quot;GET
{path,-16} {(int)response.StatusCode} {type,-25} {await response.Content.ReadAsStringAsync()}");
} await app.StopAsync(); // Controllers/ProductsController.cs using Microsoft.AspNetCore.Mvc; public record ProductDto(int Id, string Name, decimal Price); [ApiController] [Route("api/[controller]")] public class ProductsController : ControllerBase { private static readonly ProductDto[] Products = [new(1, "Wireless Mouse", 799m), new(2, "USB-C Hub", 1499m)]; [HttpGet] public IEnumerable<ProductDto> GetAll() => Products; [HttpGet("{id:int}")] public ActionResult<ProductDto> Get(int id) => Products.FirstOrDefault(p => p.Id == id) is { } product ? product : NotFound(); }
Expected output
GET /api/products    200 application/json          [{"id":1,"name":"Wireless Mouse","price":799},{"id":2,"name":"USB-C Hub","price":1499}]
GET /api/products/2  200 application/json          {"id":2,"name":"USB-C Hub","price":1499}
GET /api/products/9  404 application/problem+json  {"type":"https://tools.ietf.org/html/rfc9110#section-15.5.5","title":"Not Found","status":404,"traceId":"0HNPL7JNPL4ED:00000003"}
GET /api/status      200 application/json          {"status":"ok","version":"1.0"}
(the traceId varies per request)

Practice exercises

Show all 37 exercises
Request and Response Models

Make the API accept and return snake_case JSON (customer_id, gift_message) instead of camelCase.

DTOs

Create an UpdatePriceDto (only Price, between 1 and 1,000,000) and a PATCH /api/products/{id}/price action that changes only the price.

Model Validation

Customise the automatic 400 response so it also includes an "errorCount" extension with the number of invalid fields.

Dependency Injection

Inject the express calculator directly into an action with [FromKeyedServices("express")] and return its cost for 1 kg.

Service Layer

Add an ErrorType.Forbidden mapped to 403 and a service rule: products with id over 100 are “staff only” and cannot be reserved by customers.

ADO.NET Integration

Add GET /api/orders/{id} that returns { id, email, total } using ExecuteReaderAsync, or 404 when the order does not exist.

Entity Framework Core

Add DELETE /api/products/discontinued that deletes every product with Stock == 0 using ExecuteDeleteAsync and returns { deleted }.

SQL Server

Make the health endpoint return JSON like {"status":"Healthy","checks":{"database":"Healthy"}} using HealthCheckOptions.ResponseWriter.

Stored Procedures

Call usp_GetProductsByCategory with plain ADO.NET (CommandType.StoredProcedure) and return the product names.

CRUD API

Add GET /api/products/search?name=key that returns products whose name contains the search text.

Pagination

Add an X-Total-Count response header to the offset endpoint so clients that ignore the body can read the total.

Filtering and Sorting

Allow sorting by "stock" too, and add a filter ?names=Mouse Pad&names=USB-C Hub that returns only those products.

Searching

Change the search so every word must match: q="usb hub" returns products whose name or description contains both “usb” and “hub”.

Global Exception Handling

Make the handler map OperationCanceledException (client disconnected) to status 499 without logging it as an error, and return true without writing a body.

Logging

Wrap the order-placing code in a log scope with OrderId so every log line inside it carries that field, and enable IncludeScopes on the JSON console formatter.

Swagger and OpenAPI

Add the Scalar interactive UI in Development only, available at /scalar/v1.

JWT Authentication

Add a [Authorize] GET /api/orders/mine endpoint that returns the caller’s user id from the sub claim, and returns 401 for anonymous callers.

Role-Based Authorization

Add a policy "ReadReports" that requires the claim scope=reports:read, and use it on GET /api/reports.

CORS

Read the allowed origins from configuration (Cors:Origins array) instead of hard-coding them.

API Versioning

Add a v2-only endpoint GET /api/v2/products/{id}/reviews that does not exist in v1.

Rate Limiting

Add a fixed-window policy "login" that allows 5 requests per minute per IP address and apply it to POST /api/auth/login.

File Upload APIs

Allow uploading several images at once with IFormFileCollection and return the count of accepted files (skip non-JPEGs).

API Testing

Write curl commands that create a product, print the response headers, and then delete the product using its id.

Unit Testing

Add a Theory row proving that a gold customer buying 1 item at ₹999.99 pays ₹849.99 (rounded to 2 decimals).

Integration Testing

Add a test that posts a product with a 101-character name and asserts a 400 response with an error for Name.

Performance

Add a "Product" output-cache policy that varies by the id route value and expires after 2 minutes, and use it on GET /api/products/{id}.

Secure API Design

Let staff (role Support) read any order while customers can still only read their own. Return 404 for customers reading other orders.

Docker

Write a docker-compose.yml that starts SQL Server and the API, waits for SQL Server to be healthy, and maps the API to port 5080.

Azure Deployment

Write a GitHub Actions workflow that builds the API on push to main and deploys it to the staging slot with azure/webapps-deploy.

React + ASP.NET Core API Project

Add a deleteProduct(id) function to api.ts and a Delete button per product that removes it from the list after a 204.

Angular + ASP.NET Core API Project

Add a delete(id) method to ProductService and a Delete button in the list that removes the product from the products signal after success.

Common interview questions

Frequently asked questions

Controllers or minimal APIs?
Both are fully supported. Minimal APIs are concise for small services; controllers organise larger APIs. This track uses controllers and shows minimal API equivalents where helpful.