Student learning series · Lesson 6 of 6
Build a student API with create, read, update, delete, input validation and clear HTTP responses.
What you will learn
CRUD means Create, Read, Update and Delete. This project combines route parameters, JSON binding and response results in an in-memory student directory. It intentionally focuses on HTTP behavior before introducing a database.
1. Create the project
dotnet new web -n StudentApi
cd StudentApiReplace Program.cs with the complete code below. This learning API has no authentication and stores data only in process memory. Run it on loopback for practice; add persistence, authorization and deployment security before real use.
2. Complete Program.cs
using System.Collections.Concurrent;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var students = new ConcurrentDictionary<int, Student>();
int nextId = 0;
app.MapGet("/students", () => students.Values.OrderBy(s => s.Id));
app.MapGet("/students/{id:int}", (int id) =>
students.TryGetValue(id, out var student)
? Results.Ok(student) : Results.NotFound());
app.MapPost("/students", (StudentInput input) =>
{
if (string.IsNullOrWhiteSpace(input.Name) || input.Name.Length > 80)
return Results.BadRequest(new { error = "Name must contain 1 to 80 characters." });
int id = Interlocked.Increment(ref nextId);
var student = new Student(id, input.Name.Trim());
students[id] = student;
return Results.Created($"/students/{id}", student);
});
app.MapPut("/students/{id:int}", (int id, StudentInput input) =>
{
if (string.IsNullOrWhiteSpace(input.Name) || input.Name.Length > 80)
return Results.BadRequest(new { error = "Name must contain 1 to 80 characters." });
if (!students.TryGetValue(id, out var current)) return Results.NotFound();
var replacement = new Student(id, input.Name.Trim());
return students.TryUpdate(id, replacement, current)
? Results.Ok(replacement) : Results.Conflict();
});
app.MapDelete("/students/{id:int}", (int id) =>
students.TryRemove(id, out _) ? Results.NoContent() : Results.NotFound());
app.Run();
record Student(int Id, string Name);
record StudentInput(string? Name);
3. Run and observe
dotnet run --no-launch-profile -- --urls http://127.0.0.1:5070Stop the server with Ctrl+C before starting the next lesson on the same port. For web lessons, visit the listed GET path at http://127.0.0.1:5070 in your browser. Use an HTTP client for POST, PUT and DELETE with Content-Type: application/json.
POST /students with {"name":"Asha"} returns 201 and Location: /students/1.
GET /students/1 returns 200.
PUT /students/1 with {"name":"Ravi"} returns 200.
DELETE /students/1 returns 204.
GET /students/1 after deletion returns 404.4. Understand the code
StudentInput is bound from the JSON request body. The name check rejects null, whitespace and names longer than 80 characters with 400. Interlocked generates IDs safely; ConcurrentDictionary handles shared storage operations. TryUpdate changes only the value we read: a competing change returns 409. Results.Created supplies the new resource location.
Practice exercise
Test blank names, missing IDs and deletion twice. Add an email field with validation. Then design a persistent version using EF Core and migrations as a later project.
Self-check and answer
A blank name returns 400. A missing ID returns 404. First deletion returns 204 and the second 404. Restarting loses all records. HTTP methods are verbs; use resource paths such as /students rather than /addStudent.
Study notes
Type the example yourself, run it, change one behavior, and explain the result in your own words. Keep each lesson in its own project. These examples were compiled with .NET SDK 9.0.306; select a currently supported SDK for new study projects. The course teaches modern .NET and ASP.NET Core, rather than obsolete .NET Core 3.1 or classic Web Forms.
Return to the .NET Core student learning path · Official Microsoft documentation · .NET support policy
No comments:
Post a Comment
Please do not enter any spam link in the comment box.