Minimal API — MapGet/Post, endpoint filter, GroupRouteBuilder

~20 daq · .NET Backend · API Sifati va Dizayn

Muammodan boshlaylik

Kichik mikroservis — 4 ta endpoint. Controller yondashuvida: controller klassi, atributlar, ControllerBase, [ApiController], AddControllers, MapControllers... Oddiy "ID bo'yicha qaytar" uchun ko'p "marosim". Yangi boshlovchi uchun esa birinchi API dan oldin ko'p tushuncha.


Minimal API nima?

.NET 6 dan boshlab — endpoint larni to'g'ridan-to'g'ri Program.cs da, lambda yoki metod bilan e'lon qilish:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/salom", () => "Salom, dunyo!");

app.Run();

To'rt qator — ishlaydigan API.


Hayotiy o'xshatma

Katta restoran va ko'cha oshxonasi. Restoranda (controller) — qabulxona, ofitsiantlar, menyu kitobi, alohida zallar: katta oqim uchun tartib. Ko'cha oshxonasida (Minimal API) — bitta oyna: buyurtma berding, oldingga berildi. Taom sifati bir xil bo'lishi mumkin — farq tashkiliy tuzilmada. Va ko'cha oshxonasi ham kengayib, bir nechta oynali bo'lishi mumkin (MapGroup).


Asosiy metodlar

app.MapGet("/api/mahsulotlar", async (DokonDbContext db, CancellationToken ct) =>
    await db.Mahsulotlar.AsNoTracking().Select(m => new MahsulotDto(m.Id, m.Nomi, m.Narx)).ToListAsync(ct));

app.MapGet("/api/mahsulotlar/{id:int}", async (int id, DokonDbContext db, CancellationToken ct) =>
    await db.Mahsulotlar.FindAsync([id], ct) is { } m
        ? Results.Ok(new MahsulotDto(m.Id, m.Nomi, m.Narx))
        : Results.NotFound());

app.MapPost("/api/mahsulotlar", async (MahsulotYaratDto dto, DokonDbContext db, CancellationToken ct) =>
{
    var m = new Mahsulot { Nomi = dto.Nomi, Narx = dto.Narx };
    db.Mahsulotlar.Add(m);
    await db.SaveChangesAsync(ct);
    return Results.Created(
quot;/api/mahsulotlar/{m.Id}", new MahsulotDto(m.Id, m.Nomi, m.Narx)); }); app.MapPut("/api/mahsulotlar/{id:int}", ...); app.MapDelete("/api/mahsulotlar/{id:int}", ...);

Parametrlar qayerdan keladi

Manba Avtomatik Aniq
Route {id} nomi mos kelsa [FromRoute]
Query ?sahifa=2 oddiy turlar [FromQuery]
Body (JSON) murakkab tur [FromBody]
Sarlavha — [FromHeader]
DI servis ro'yxatdan o'tgan tur [FromServices]
Maxsus HttpContext, CancellationToken, ClaimsPrincipal —

Bir nechta parametr — [AsParameters]

public record MahsulotSorov(string? Qidiruv, int Sahifa = 1, int Hajm = 20);

app.MapGet("/api/mahsulotlar", async ([AsParameters] MahsulotSorov s, DokonDbContext db) => ...);

TypedResults — turi aniq javoblar

app.MapGet("/api/mahsulotlar/{id:int}",
    async Task<Results<Ok<MahsulotDto>, NotFound>> (int id, DokonDbContext db, CancellationToken ct) =>
    {
        var m = await db.Mahsulotlar.FindAsync([id], ct);
        return m is null ? TypedResults.NotFound() : TypedResults.Ok(new MahsulotDto(m.Id, m.Nomi, m.Narx));
    });

Results<Ok<T>, NotFound> — OpenAPI hujjatiga barcha mumkin bo'lgan javoblar avtomatik tushadi (72-dars), unit testda ham turini tekshirish oson.


MapGroup — guruhlash

var mahsulotlar = app.MapGroup("/api/mahsulotlar")
    .WithTags("Mahsulotlar")
    .RequireAuthorization();

mahsulotlar.MapGet("/", Royxat).AllowAnonymous();
mahsulotlar.MapGet("/{id:int}", Ol).AllowAnonymous();
mahsulotlar.MapPost("/", Yarat).RequireAuthorization("MahsulotBoshqarish");
mahsulotlar.MapDelete("/{id:int}", Ochir).RequireAuthorization("MahsulotBoshqarish");

Umumiy prefiks, teg, avtorizatsiya, rate limit, CORS — bir joyda.

Program.cs ni tartibli saqlash

public static class MahsulotEndpointlari
{
    public static RouteGroupBuilder MapMahsulotlar(this IEndpointRouteBuilder app)
    {
        var g = app.MapGroup("/api/mahsulotlar").WithTags("Mahsulotlar");
        g.MapGet("/", Royxat);
        g.MapGet("/{id:int}", Ol).WithName("MahsulotOl");
        g.MapPost("/", Yarat);
        return g;
    }

    static async Task<Ok<List<MahsulotDto>>> Royxat(MahsulotService s, CancellationToken ct) =>
        TypedResults.Ok(await s.RoyxatAsync(ct));

    static async Task<Results<Ok<MahsulotDto>, NotFound>> Ol(int id, MahsulotService s, CancellationToken ct) =>
        await s.OlAsync(id, ct) is { } m ? TypedResults.Ok(m) : TypedResults.NotFound();

    static async Task<Created<MahsulotDto>> Yarat(MahsulotYaratDto dto, MahsulotService s, CancellationToken ct)
    {
        var m = await s.YaratAsync(dto, ct);
        return TypedResults.Created(
quot;/api/mahsulotlar/{m.Id}", m); } }
// Program.cs
app.MapMahsulotlar();
app.MapBuyurtmalar();

Endpoint filter — "middleware" endpoint uchun

public class ValidatsiyaFilter<T>(IValidator<T> validator) : IEndpointFilter
{
    public async ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext ctx, EndpointFilterDelegate next)
    {
        var arg = ctx.Arguments.OfType<T>().FirstOrDefault();
        if (arg is null) return TypedResults.BadRequest();

        var natija = await validator.ValidateAsync(arg);
        if (!natija.IsValid)
            return TypedResults.ValidationProblem(natija.ToDictionary());

        return await next(ctx);
    }
}
mahsulotlar.MapPost("/", Yarat).AddEndpointFilter<ValidatsiyaFilter<MahsulotYaratDto>>();

Filter — endpoint dan oldin va keyin ishlaydi: validatsiya, log, vaqtni o'lchash. Guruhga qo'shilsa — guruhdagi hammasiga.

[!INFO] .NET 10 da Minimal API uchun o'rnatilgan validatsiya qo'shilgan: builder.Services.AddValidation(); — DataAnnotations atributlari ([Required], [Range]) bilan belgilangan parametrlar avtomatik tekshiriladi va 400 ProblemDetails qaytariladi.


Controller yoki Minimal API?

Controller Minimal API
Kod hajmi Ko'proq Kam
Performance Yaxshi Biroz tezroq, Native AOT bilan mos
Tuzilma Konvensiyalar tayyor O'zingiz tashkil qilasiz (MapGroup, kengaytmalar)
Filterlar Action/Result filter lar Endpoint filter lar
Model binding Juda boy Yetarli, ba'zi farqlar bilan

Microsoft yangi loyihalar uchun Minimal API ni tavsiya qiladi, lekin Controller lar to'liq qo'llab-quvvatlanadi. Ikkalasi bitta loyihada birga ishlaydi. Katta, mavjud jamoaviy loyihada — jamoa konvensiyasi muhimroq.


Tez-tez uchraydigan xatolar

Hammasi Program.cs da — 800 qatorlik fayl; kengaytma metodlar va MapGroup ga ajrating.

Results.Ok(entity) — entity to'g'ridan-to'g'ri; DTO (45-dars).

Results va TypedResults aralash — hujjatda javob turlari yo'qoladi; TypedResults ni afzal ko'ring.


Xulosa

Minimal API — endpoint lar MapGet/MapPost/MapPut/MapDelete bilan, lambda yoki metod orqali; parametrlar route, query, body va DI dan avtomatik. TypedResults + Results<A, B> — turi aniq javoblar, OpenAPI uchun avtomatik.

MapGroup — umumiy prefiks, teg, avtorizatsiya; kengaytma metodlar bilan Program.cs tartibli. Endpoint filter (IEndpointFilter) — validatsiya va kesishuvchi mantiq. Controller va Minimal API — ikkalasi to'liq yechim, birga ishlatish mumkin.


Keyingi bo'lim

76-dars: Unit Test — xUnit, Fact, Theory, Assert