Lesson 5 of 40 Web Development Intermediate 50 min

ASP.NET Core 10 Web APIs

Build a clean, testable REST API in Visual Studio 2026 using Minimal APIs, route groups, dependency injection, validation, Problem Details, OpenAPI documentation, security basics, and deployment-ready project habits.

RESTGET, POST, PUT, DELETE endpoints
OpenAPIDocument and test your API
AOT ReadyPrepare lean Minimal APIs
Visual Studio 2026 · OrdersApi
GET/api/orders
GET/{id}
POST/api/orders
PUT/{id}
DEL/{id}
var orders = app.MapGroup("/api/orders")
  .WithTags("Orders")
  .RequireAuthorization();

orders.MapGet("/{id:int}", async (int id, IOrderService svc) =>
  await svc.FindAsync(id) is {} order
    ? Results.Ok(order)
    : Results.NotFound());

// Run, test, document, and debug in one IDE.
API roadmap

What You Will Build in This Lesson

In this lesson, you will build the structure of a small Orders API. The goal is not just to create endpoints, but to create a production-minded API that is easy to debug, document, test, and extend in later lessons.

Endpoint designUse clean route groups such as /api/orders.
Service layerKeep business logic outside endpoint handlers.
Client-ready docsExpose OpenAPI so callers can explore and test the API.
Important learning path: This lesson creates the Web API foundation. Lesson 6 continues naturally with Entity Framework Core 10 so your API can work with a real database.
Project setup

Part 1: Create an ASP.NET Core Web API Project

In Visual Studio 2026, create a new ASP.NET Core Web API project. For beginners, start with the standard Web API template. For lightweight cloud services, you can also choose a Minimal API style project.

1

Create the project

Select ASP.NET Core Web API, name it OrdersApi, and choose the latest .NET runtime available in your setup.

2

Enable OpenAPI

Keep OpenAPI support enabled so Visual Studio can help you test and document endpoints.

3

Run the app

Press F5 for debugging or Ctrl + F5 to run without the debugger.

Beginner habit: Keep the Output, Error List, Terminal, and Solution Explorer windows visible. They help you understand what Visual Studio is doing when the API builds and runs.
Minimal APIs

Part 2: Use Route Groups to Organize Endpoints

Route groups keep related endpoints together. Instead of repeating /api/orders, authorization, tags, and OpenAPI settings on every endpoint, define them once on the group.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddOpenApi();
builder.Services.AddScoped<IOrderService, OrderService>();

var app = builder.Build();
app.MapOpenApi();
app.UseAuthorization();

var orders = app.MapGroup("/api/orders")
  .WithTags("Orders")
  .WithOpenApi();

orders.MapGet("/", async (IOrderService service) =>
  Results.Ok(await service.GetAllAsync()));

orders.MapGet("/{id:int}", async (int id, IOrderService service) =>
  await service.FindAsync(id) is { } order
    ? Results.Ok(order)
    : Results.NotFound());

app.Run();

This structure makes the API easier to read because each route group represents a resource area: orders, customers, products, reports, accounts, and so on.

Contracts and validation

Part 3: Design Request and Response Models

A production API should not expose internal database entities directly. Use small request and response models so your API contract remains stable even when the database design changes.

public sealed record CreateOrderRequest(
  [Required] string CustomerName,
  [Range(1, 999999)] decimal Total);

public sealed record OrderResponse(
  int Id,
  string CustomerName,
  decimal Total,
  string Status);
Why this matters: A clear API contract helps web apps, mobile apps, desktop apps, and external systems use your API without guessing field names or error formats.
Dependency injection

Part 4: Move Logic into a Service Class

Minimal APIs are cleanest when endpoint handlers stay short. Put business rules, database queries, and validation logic into services that are injected into endpoints.

public interface IOrderService
{
  Task<IReadOnlyList<OrderResponse>> GetAllAsync();
  Task<OrderResponse?> FindAsync(int id);
  Task<OrderResponse> CreateAsync(CreateOrderRequest request);
}

public sealed class OrderService : IOrderService
{
  // In Lesson 6, this can use EF Core instead of an in-memory list.
}
LayerResponsibilityExample
EndpointHTTP routing and response shapeMapGet, MapPost, Results.Ok
ServiceBusiness rules and workflowCheck order status, calculate totals
Repository/DataDatabase accessEF Core queries in Lesson 6
Consistent errors

Part 5: Use Problem Details for API Errors

Clients should receive predictable error responses. Problem Details is a standard JSON format for describing HTTP API errors. It is especially useful for validation failures, missing resources, and server exceptions.

builder.Services.AddProblemDetails();

var app = builder.Build();
app.UseExceptionHandler();

orders.MapGet("/{id:int}", async (int id, IOrderService service) =>
  await service.FindAsync(id) is { } order
    ? Results.Ok(order)
    : Results.Problem("Order was not found.", statusCode: 404));
Avoid: Do not return raw exception messages to public clients. Log detailed errors on the server, but return safe and consistent responses to users and client applications.
Documentation

Part 6: Add OpenAPI and Scalar UI

OpenAPI documents describe your endpoints, request bodies, response types, authentication requirements, and error formats. Visual Studio can help you test your API during development, while OpenAPI files can also be used to generate client SDKs.

builder.Services.AddOpenApi();

var app = builder.Build();
app.MapOpenApi(); // Usually /openapi/v1.json

For an interactive API documentation page, you can integrate Scalar by adding the Scalar.AspNetCore package and mapping its API reference page.

using Scalar.AspNetCore;

builder.Services.AddOpenApi();

app.MapOpenApi();
app.MapScalarApiReference();
Use OpenAPI forTesting endpoints, sharing API contracts, documenting models, generating clients.
Use Scalar forInteractive browsing and trying API calls from a clean web UI.
Security basics

Part 7: Add Authorization Habits Early

Even a simple API should be designed with security in mind. In early lessons, the goal is to understand where authorization belongs. Later, you can connect JWT bearer tokens, Microsoft Entra ID, Identity, or another provider.

builder.Services.AddAuthentication();
builder.Services.AddAuthorization();

var orders = app.MapGroup("/api/orders")
  .RequireAuthorization();

orders.MapPost("/", async (CreateOrderRequest request, IOrderService service) =>
{
  var created = await service.CreateAsync(request);
  return Results.Created($"/api/orders/{created.Id}", created);
});
AreaGood habit
InputValidate request models and reject bad data early.
AuthorizationProtect write endpoints such as POST, PUT, and DELETE.
SecretsNever hard-code connection strings, API keys, or tokens in source code.
LoggingLog useful diagnostics, but avoid storing passwords, tokens, or private user data.
Performance and publishing

Part 8: Native AOT Readiness

Native AOT can publish an ASP.NET Core app as a native executable. It can reduce startup time and memory usage for suitable services, but it also requires careful library choices and testing.

<!-- OrdersApi.csproj -->
<PropertyGroup>
  <PublishAot>true</PublishAot>
  <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>
Production note: Native AOT is not a switch to turn on blindly. Test JSON serialization, reflection-heavy libraries, authentication packages, and monitoring tools before using it for a live service.
Run and test

Part 9: Test the API in Visual Studio 2026

Visual Studio can run your Web API, attach the debugger, show logs, inspect exceptions, and help you test endpoints. You can also keep an .http file in your solution to test repeatable API requests.

@host = https://localhost:7001

GET {{host}}/api/orders
Accept: application/json

###
POST {{host}}/api/orders
Content-Type: application/json

{
  "customerName": "Dr. Liew",
  "total": 250.00
}
Debugging tipPut breakpoints inside endpoint handlers and service methods. Inspect route parameters, request bodies, and return values.
Copilot tipAsk Copilot to explain failing requests, suggest test cases, or generate an HTTP file for your endpoints.
Production checklist

Part 10: Before You Deploy an API

Checklist itemWhy it matters
Health endpointHelps cloud platforms and monitoring tools check whether the API is alive.
Central error handlingPrevents inconsistent error responses across endpoints.
OpenAPI contractHelps developers and client apps understand how to call the API.
Authentication and authorizationProtects private data and write operations.
Logging and diagnosticsHelps you investigate failures without guessing.
Unit and integration testsReduces the risk of breaking routes, validation, and business rules.

Hands-On Exercise: Build a Small Orders API

Create a Minimal API project called OrdersApi. Then complete these tasks:

  1. Add route group /api/orders.
  2. Add GET /api/orders and GET /api/orders/{id}.
  3. Add a CreateOrderRequest model with validation attributes.
  4. Add POST /api/orders and return Results.Created.
  5. Add OpenAPI support and test the generated endpoint document.
  6. Create an orders.http file and test at least two requests.
Visual Studio 2026 Made Easy book cover
Recommended Book

Visual Studio 2026 Made Easy

Continue learning Visual Studio 2026 with a structured companion book covering C#, VB.NET, Python, JavaScript, C++, .NET 10, debugging, deployment, and practical projects.