Minimal API Endpoint Design¶
Minimal API’ler, hafif ve hızlı endpoint’ler oluşturur; yanlış tasarım karmaşık ve bakımı zor koda yol açar.
1. Tüm Endpoint’leri Program.cs’e Yığmak¶
❌ Yanlış Kullanım: Tüm endpoint tanımlarını tek dosyada tutmak.
var app = builder.Build();
app.MapGet("/api/products", async (AppDbContext db) => await db.Products.ToListAsync());
app.MapGet("/api/products/{id}", async (int id, AppDbContext db) => await db.Products.FindAsync(id));
app.MapPost("/api/products", async (Product product, AppDbContext db) => { /* ... */ });
app.MapGet("/api/orders", async (AppDbContext db) => await db.Orders.ToListAsync());
app.MapPost("/api/orders", async (Order order, AppDbContext db) => { /* ... */ });
// 100+ endpoint tek dosyada
✅ İdeal Kullanım: Route group extension method’ları ile organize edin.
// Program.cs
app.MapProductEndpoints();
app.MapOrderEndpoints();
// ProductEndpoints.cs
public static class ProductEndpoints
{
public static void MapProductEndpoints(this WebApplication app)
{
var group = app.MapGroup("/api/products").WithTags("Products");
group.MapGet("/", GetAll);
group.MapGet("/{id}", GetById);
group.MapPost("/", Create);
}
private static async Task<Ok<List<ProductDto>>> GetAll(IProductService service)
{
var products = await service.GetAllAsync();
return TypedResults.Ok(products);
}
private static async Task<Results<Ok<ProductDto>, NotFound>> GetById(int id, IProductService service)
{
var product = await service.GetByIdAsync(id);
return product is not null ? TypedResults.Ok(product) : TypedResults.NotFound();
}
}
2. Typed Results Kullanmamak¶
❌ Yanlış Kullanım: IResult ile belirsiz dönüş tipi.
app.MapGet("/api/products/{id}", async (int id, AppDbContext db) =>
{
var product = await db.Products.FindAsync(id);
if (product == null) return Results.NotFound();
return Results.Ok(product);
}); // OpenAPI dökümantasyonu dönüş tipini bilemez
✅ İdeal Kullanım: TypedResults ile OpenAPI uyumlu endpoint yazın.
app.MapGet("/api/products/{id}", async Task<Results<Ok<ProductDto>, NotFound>> (int id, IProductService service) =>
{
var product = await service.GetByIdAsync(id);
return product is not null
? TypedResults.Ok(product)
: TypedResults.NotFound();
}).WithName("GetProductById")
.WithOpenApi();
3. Endpoint Filter Kullanmamak¶
❌ Yanlış Kullanım: Her endpoint’te validasyon tekrarlamak.
app.MapPost("/api/products", async (CreateProductDto dto, AppDbContext db) =>
{
if (string.IsNullOrEmpty(dto.Name)) return Results.BadRequest("Ad boş olamaz");
if (dto.Price <= 0) return Results.BadRequest("Fiyat pozitif olmalı");
// ...
});
✅ İdeal Kullanım: Endpoint filter ile validasyonu merkezileştirin.
public class ValidationFilter<T> : IEndpointFilter
{
public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context,
EndpointFilterDelegate next)
{
var validator = context.HttpContext.RequestServices.GetService<IValidator<T>>();
if (validator is null) return await next(context);
var argument = context.Arguments.OfType<T>().FirstOrDefault();
if (argument is null) return await next(context);
var result = await validator.ValidateAsync(argument);
if (!result.IsValid)
return TypedResults.ValidationProblem(result.ToDictionary());
return await next(context);
}
}
app.MapPost("/api/products", Create)
.AddEndpointFilter<ValidationFilter<CreateProductDto>>();
4. Route Group Özelliklerini Kullanmamak¶
❌ Yanlış Kullanım: Her endpoint’e ayrı ayrı attribute eklemek.
app.MapGet("/api/admin/users", GetUsers).RequireAuthorization("Admin");
app.MapPost("/api/admin/users", CreateUser).RequireAuthorization("Admin");
app.MapDelete("/api/admin/users/{id}", DeleteUser).RequireAuthorization("Admin");
✅ İdeal Kullanım: Route group ile ortak konfigürasyonu bir kez tanımlayın.
var admin = app.MapGroup("/api/admin")
.RequireAuthorization("Admin")
.WithTags("Admin");
admin.MapGet("/users", GetUsers);
admin.MapPost("/users", CreateUser);
admin.MapDelete("/users/{id}", DeleteUser);
var publicApi = app.MapGroup("/api/public")
.AllowAnonymous()
.WithTags("Public");
publicApi.MapGet("/products", GetProducts);
publicApi.MapGet("/categories", GetCategories);
5. Dependency Injection’ı Yanlış Kullanmak¶
❌ Yanlış Kullanım: HttpContext’ten manuel servis çözümleme.
app.MapGet("/api/products", async (HttpContext context) =>
{
var service = context.RequestServices.GetRequiredService<IProductService>();
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
return await service.GetAllAsync();
});
✅ İdeal Kullanım: Parametrelerde doğrudan servis injection kullanın.
app.MapGet("/api/products", async (
IProductService service,
ILogger<Program> logger,
CancellationToken ct) =>
{
logger.LogInformation("Ürünler listeleniyor");
return TypedResults.Ok(await service.GetAllAsync(ct));
});