dotnet --version
Create a .NET 8 Minimal API with route groups, typed results, validation, and OpenAPI documentation.
1 using Microsoft.AspNetCore.Http.HttpResults; 2 3 public static class ProductEndpoints 4 { 5 private static readonly List<Product> Products = [ 6 new(1, "Widget A", 9.99m), 7 new(2, "Widget B", 19.99m), 8 ]; 9 10 public static RouteGroupBuilder MapProductEndpoints(this IEndpointRouteBuilder app) 11 { 12 var group = app.MapGroup("/api/products") 13 .WithTags("Products") 14 .WithOpenApi(); 15 16 group.MapGet("/", GetAll).WithName("GetAllProducts"); 17 group.MapGet("/{id:int}", GetById).WithName("GetProductById"); 18 group.MapPost("/", Create).WithName("CreateProduct"); 19 group.MapPut("/{id:int}", Update).WithName("UpdateProduct"); 20 group.MapDelete("/{id:int}", Delete).WithName("DeleteProduct"); 21 22 return group; 23 } 24 25 static Ok<List<Product>> GetAll() => TypedResults.Ok(Products); 26 27 static Results<Ok<Product>, NotFound> GetById(int id) 28 { 29 var product = Products.FirstOrDefault(p => p.Id == id); 30 return product is null ? TypedResults.NotFound() : TypedResults.Ok(product); 31 } 32 33 static Results<Created<Product>, ValidationProblem> Create(ProductCreate input) 34 { 35 if (string.IsNullOrWhiteSpace(input.Name)) 36 return TypedResults.ValidationProblem(new Dictionary<string, string[]> 37 { 38 { "name", ["Name is required."] } 39 }); 40 41 var product = new Product(Products.Max(p => p.Id) + 1, input.Name, input.Price); 42 Products.Add(product); 43 return TypedResults.Created($"/api/products/{product.Id}", product); 44 } 45 46 static Results<Ok<Product>, NotFound, ValidationProblem> Update(int id, ProductCreate input) 47 { 48 var existing = Products.FirstOrDefault(p => p.Id == id); 49 if (existing is null) return TypedResults.NotFound(); 50 if (input.Price < 0) 51 return TypedResults.ValidationProblem(new Dictionary<string, string[]> 52 { 53 { "price", ["Price cannot be negative."] } 54 }); 55 Products[Products.IndexOf(existing)] = existing with { Name = input.Name, Price = input.Price }; 56 return TypedResults.Ok(Products.First(p => p.Id == id)); 57 } 58 59 static Results<NoContent, NotFound> Delete(int id) 60 { 61 var product = Products.FirstOrDefault(p => p.Id == id); 62 if (product is null) return TypedResults.NotFound(); 63 Products.Remove(product); 64 return TypedResults.NoContent(); 65 } 66 } 67 68 public record Product(int Id, string Name, decimal Price); 69 public record ProductCreate(string Name, decimal Price); 70
Sign in to share your feedback and join the discussion.