RE:NODE

აპლიკაციები11 წუთის საკითხავი

Minimal API-ები .NET-ში: route-ები, ვალიდაცია და OpenAPI

ააწყე რეალური HTTP API .NET-ის minimal API-ებით: პარამეტრების binding, TypedResults, route group-ები, ვალიდაცია, problem details, OpenAPI და როდის სჯობს controller-ები.

0 მკითხველი

Minimal API-ები ASP.NET Core-ია controller-ების ცერემონიის გარეშე: URL-სა და HTTP მეთოდს ფუნქციას უკავშირებ, framework კი პარამეტრებს აბამს, შედეგს ასერიალიზებს და უშვებს იმავე middleware-ს, ავთენტიფიკაციას და dependency injection-ს, რასაც ნებისმიერი სხვა ASP.NET Core აპლიკაცია. ნებისმიერი ზომის API-სთვის, რამდენიმე ასეულ endpoint-მდე, მიმდინარე .NET-ში ისინი ნაგულისხმევი არჩევანია. ის, რაც მათ production-ისთვის მზად ხდის - route group-ები endpoint-ების მოსაწესრიგებლად, TypedResults, რომ დასაბრუნებელი ტიპები თავად აღწერდნენ საკუთარ თავს, ჩაშენებული ვალიდაცია (ახალი .NET 10-ში), problem details შეცდომებისთვის და დაგენერირებული OpenAPI დოკუმენტი - თითოეული ერთი-ორი ხაზია, და ეს პოსტი ყველა მათგანს აჩვენებს.

მაგალითები .NET 10-ზეა გათვლილი, მიმდინარე long-term support გამოშვებაზე. სადაც ფუნქცია .NET 8-ზე გვიან გამოჩნდა, ტექსტი ამას ამბობს.

Minimal API მთლიანი ვებ აპლიკაციაა#

ყველაზე პატარა გამოსადეგი ვარიანტი ოთხი ხაზია:

Program.cs
var builder = WebApplication.CreateBuilder(args);var app = builder.Build();app.MapGet("/healthz", () => Results.Ok(new { ok = true }));app.Run();

ეს არის Kestrel, სრული კონფიგურაციის სისტემა, ლოგირება, dependency injection და middleware pipeline - იგივე ჰოსტი, რომელზეც MVC აპლიკაცია მუშაობს, იმ ნაწილების გარეშე, რომლებიც controller-ებს reflection-ით პოულობენ. ასე დაწყებას არც წარმადობის და არც შესაძლებლობების ფასი არ აქვს: ავთენტიფიკაციას, rate limiting-ს, EF Core-ს, background სერვისებს და controller-ებსაც კი მოგვიანებით იმავე აპლიკაციას დაამატებ.

endpoint-ის handler-ები ჩვეულებრივი delegate-ებია. ერთი ხაზისთვის lambda კარგია; ყველაფრისთვის, რაც უფრო გრძელია, route მიუთითე სტატიკურ მეთოდზე, რომ handler-ის წაკითხვა, ტესტირება და stack trace-ში პოვნა შესაძლებელი იყოს:

csharp
app.MapGet("/orders/{id:int}", OrderEndpoints.GetById);static class OrderEndpoints{    public static async Task<Results<Ok<OrderDto>, NotFound>> GetById(        int id, ShopDb db, CancellationToken ct)    {        var order = await db.Orders.AsNoTracking()            .Where(o => o.Id == id)            .Select(o => new OrderDto(o.Id, o.Total, o.Status))            .FirstOrDefaultAsync(ct);        return order is null ? TypedResults.NotFound() : TypedResults.Ok(order);    }}

JSON-ს System.Text.Json ამუშავებს ვებისთვის განკუთვნილი ნაგულისხმევებით: property-ების სახელები camelCase-ით იწერება, შემავალ მონაცემებზე დამთხვევა რეგისტრს არ ითვალისწინებს, ხოლო რიცხვების წაკითხვა სტრიქონებიდანაც შეიძლება. ორ პარამეტრზე გადაწყვეტილება პირველივე დღეს ღირს მიიღო და არა მაშინ, როცა კლიენტები უკვე გამოსავალზე იქნებიან დამოკიდებული. Enum-ები რიცხვებად სერიალიზდება, თუ converter-ს არ დაამატებ, რაც პასუხებს ძნელად წასაკითხს ხდის და მყიფეს, როცა ვინმე enum-ს გადაალაგებს. ხოლო null property-ები გამოსავალში იწერება, თუ serializer-ს არ ეტყვი, გამოტოვოს ისინი. ორივე ერთ ადგილას ცხოვრობს:

csharp
builder.Services.ConfigureHttpJsonOptions(o =>{    o.SerializerOptions.Converters.Add(new JsonStringEnumConverter());    o.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;});

ეს პარამეტრები მხოლოდ minimal API endpoint-ებზე მოქმედებს. თუ იმავე აპლიკაციას controller-ებიც აქვს, ისინი ცალკე კონფიგურირდება AddControllers().AddJsonOptions(...)-ით, და ამის დავიწყება არის ის, თუ როგორ ამთავრებს ერთი აპლიკაცია JSON-ის ორი სტილით.

Route-ები და პარამეტრების binding#

handler-ის თითოეული პარამეტრი საიდანღაც ივსება, და framework წყაროს პარამეტრის ტიპითა და სახელით ასკვნის. აშკარა ატრიბუტები ამ დასკვნას აუქმებს.

წყაროროდის ასკვნისაშკარა ატრიბუტი
Route-ის მნიშვნელობასახელი ემთხვევა {segment}-ს[FromRoute]
Query stringმარტივი ტიპი, route-თან დამთხვევის გარეშე[FromQuery]
Headerარასოდეს[FromHeader(Name = "X-Tenant")]
JSON bodyრთული ტიპი POST, PUT, PATCH-ზე[FromBody]
ფორმაარასოდეს[FromForm] (.NET 8 და უფრო ახალი)
სერვისებიტიპი DI-შია რეგისტრირებული[FromServices]
სპეციალური ტიპებიHttpContext, CancellationToken, ClaimsPrincipal...არ სჭირდება

Route-ის შეზღუდვები, როგორიცაა {id:int}, {slug:alpha} და {page:int:min(1)}, შეუსაბამო URL-ებს 404-ით უარყოფს, სანამ შენი კოდი გაეშვება. ისინი routing-ისთვისაა და არა ვალიდაციისთვის: შეზღუდვის ჩავარდნა ნიშნავს "ეს URL არ არსებობს", რაც სხვა პასუხია, ვიდრე "შენი შეყვანილი მონაცემები არასწორია".

handler-ებში, რომლებიც I/O-ს აკეთებენ, ყოველთვის მიიღე CancellationToken და გადაეცი ის შემდეგ ფენას. როცა კლიენტი კავშირს წყვეტს, ტოკენი უქმდება და მონაცემთა ბაზის query ჩერდება და არ სრულდება არავისთვის.

Nullability კონტრაქტის ნაწილია. int page-ად გამოცხადებული პარამეტრი სავალდებულოა: მოთხოვნა ?page=-ის გარეშე 400-ს იღებს, სანამ handler გაეშვება. გამოაცხადე ის int? page-ად ან მიეცი ნაგულისხმევი მნიშვნელობა, int page = 1, და ის არასავალდებულო გახდება. იგივე ეხება body-ებს - არა-nullable body პარამეტრი ცარიელ მოთხოვნას უარყოფს. Development-ში 400 პასუხი ხსნის, რომელი პარამეტრის binding ჩავარდა; Production-ში ის შეგნებულად მოკლეა, ამიტომ binding-ის ჩავარდნები ლოგში ჩაწერე, თუ მათი ნახვა გჭირდება.

როცა handler ბევრ პარამეტრს იღებს, დააჯგუფე ისინი ტიპში [AsParameters]-ით:

csharp
public record OrderQuery(int Page = 1, int PageSize = 20, string? Status = null);app.MapGet("/orders", ([AsParameters] OrderQuery q, ShopDb db) => /* ... */);

.NET 8-იდან endpoint-ები, რომლებიც ფორმებს ან IFormFile ატვირთვებს აბამენ, მონიშნულია, როგორც antiforgery ვალიდაციის მომთხოვნი. API-სთვის, რომელსაც ბრაუზერები კი არა, სხვა პროგრამები იძახებენ, ამ endpoint-ზე გამოიძახე .DisableAntiforgery(); ბრაუზერის ფორმისთვის დაარეგისტრირე antiforgery და ტოკენი ფორმასთან ერთად გაგზავნე.

შედეგების დაბრუნება: Results და TypedResults#

handler-ს შეუძლია დააბრუნოს უბრალო ობიექტი (JSON-ად სერიალიზდება 200 სტატუსით), სტრიქონი (ტექსტად იწერება) ან IResult. შედეგებს ორი სტატიკური კლასი ქმნის:

  • Results.Ok(x), Results.NotFound() და მსგავსები IResult-ს აბრუნებენ. მოქნილია, მაგრამ framework ხელმოწერიდან ვერ ხედავს, რას აბრუნებს endpoint.
  • TypedResults.Ok(x) და მსგავსები კონკრეტულ ტიპებს აბრუნებენ, როგორიცაა Ok<OrderDto>. union დასაბრუნებელ ტიპთან ერთად, Results<Ok<OrderDto>, NotFound, ValidationProblem>, თავად ხელმოწერა ჩამოთვლის ყველა შესაძლო პასუხს, და OpenAPI გენერატორი მათ დამატებითი ატრიბუტების გარეშე აღწერს.

უპირატესობა მიანიჭე TypedResults-ს union დასაბრუნებელი ტიპით. მას კომპილატორი ამოწმებს - union-ში არარსებული სტატუსის დაბრუნება კომპილაციის შეცდომაა - და ის API-ის აღწერას უფასოდ ზუსტს ხდის. ის ასევე handler-ების unit ტესტირებას ამარტივებს, რადგან შედეგი ტიპიზებული ობიექტია, რომელზეც assert-ის გაკეთება შეგიძლია.

Route group-ები და რეალური პროექტის ორგანიზება#

MapGroup endpoint-ების ნაკრებს საერთო პრეფიქსსა და საერთო კონფიგურაციას აძლევს:

Program.cs
var api = app.MapGroup("/api");api.MapGroup("/orders")   .WithTags("Orders")   .RequireAuthorization()   .MapOrderEndpoints();api.MapGroup("/products")   .WithTags("Products")   .MapProductEndpoints();
Orders/OrderEndpoints.cs
public static class OrderEndpointsExtensions{    public static RouteGroupBuilder MapOrderEndpoints(this RouteGroupBuilder group)    {        group.MapGet("/", OrderEndpoints.List);        group.MapGet("/{id:int}", OrderEndpoints.GetById);        group.MapPost("/", OrderEndpoints.Create);        group.MapDelete("/{id:int}", OrderEndpoints.Delete)             .RequireAuthorization("admin");        return group;    }}

ეს ის სტრუქტურაა, რომელიც minimal API-ს ორმოცდაათ endpoint-ზეც წაკითხვადს ტოვებს: ერთი ფაილი თითო ფუნქციონალზე, extension მეთოდი, რომელიც ამ ფუნქციონალის route-ებს აკავშირებს, და Program.cs, რომელიც სარჩევამდეა დაყვანილი. ყველაფერი, რაც group-ზე ვრცელდება - ავტორიზაცია, rate limiting, ფილტრები, OpenAPI tag-ები - მის ყოველ endpoint-ზე ვრცელდება, ხოლო ჩადგმული group-ები მშობლებისგან მემკვიდრეობით იღებენ.

endpoint-ები HTTP-ით გატესტე და არა მხოლოდ handler-ების ცალკე გამოძახებით, ყოველ შემთხვევაში მნიშვნელოვანი გზებისთვის. Microsoft.AspNetCore.Mvc.Testing პაკეტი მთელ აპლიკაციას მეხსიერებაში უშვებს WebApplicationFactory<Program>-ით, ამიტომ ტესტი routing-ს, binding-ს, ვალიდაციას, ფილტრებსა და სერიალიზაციას ზუსტად ისე ამოწმებს, როგორც კლიენტი, პორტის გახსნის გარეშე. factory-ის ConfigureWebHost-ში მონაცემთა ბაზა სატესტო ინსტანციით ჩაანაცვლე, და რამდენიმე ათეული ასეთი ტესტი იჭერს რეგრესიების უმეტესობას, რასაც refactor შემოიტანს. სტატიკურ მეთოდებად დაწერილი handler-ები, რომლებიც TypedResults-ს აბრუნებენ, ასევე შეიძლება პირდაპირ unit ტესტით შემოწმდეს, რაც ბიზნეს წესებისთვის უფრო სწრაფია.

ვალიდაცია#

წლების განმავლობაში minimal API-ებს ჩაშენებული ვალიდაცია არ ჰქონდა: მოთხოვნის ტიპზე data annotation-ები უბრალოდ იგნორირდებოდა, და ხალხი მათ გასაშვებად ფილტრს ან ბიბლიოთეკას ამატებდა. .NET 10 ამას framework-ში ამატებს:

csharp
builder.Services.AddValidation();public class CreateOrder{    [Required, EmailAddress] public string CustomerEmail { get; set; } = "";    [Range(1, 100)] public int Quantity { get; set; }    [StringLength(200)] public string? Note { get; set; }}

ამის რეგისტრაციის შემდეგ მოთხოვნა, რომლის body წესს არღვევს, იღებს 400 პასუხს problem details body-ით, სადაც ყოველი ჩავარდნილი ველია ჩამოთვლილი, და handler საერთოდ არ გამოიძახება. ის მუშაობს პარამეტრებზე, მოთხოვნის body-ებსა და ჩადგმულ ტიპებზე, და მას source generator მართავს, ამიტომ runtime reflection-ზე დამოკიდებული არ არის. შეამოწმე დოკუმენტაცია შენი ზუსტი ვერსიისთვის: დეტალები .NET 10-ის preview ციკლის განმავლობაში შეიცვალა.

.NET 8-სა და 9-ზე სამი ვარიანტი გაქვს. endpoint ფილტრი, რომელიც Validator.TryValidateObject-ს უშვებს და TypedResults.ValidationProblem(...)-ს აბრუნებს. პატარა MiniValidation ბიბლიოთეკა, რომელიც იგივეს აკეთებს ჩადგმული ობიექტების მხარდაჭერით. ან FluentValidation, თუ წესები კოდში გინდა და არა ატრიბუტებში, რომელიც ჩვეულებრივ ფილტრიდან გამოიძახება. სამივე ვალიდაციას თავად handler-ის გარეთ ტოვებს.

რომელიც არ უნდა გამოიყენო, ვალიდაცია კიდეზე გააკეთე და შემდეგ ტიპს ენდე. მონაცემთა ბაზამ მაინც უნდა აიძულოს ის, რისი აძულებაც შეუძლია - not-null სვეტები, უნიკალური ინდექსები, foreign key-ები - რადგან API-ში ვალიდაცია ერთ შესასვლელს იცავს, მონაცემთა ბაზა კი ყველას.

შეცდომები და problem details#

Production-ში minimal API-ში დაუმუშავებელი გამონაკლისი ნაგულისხმევად ცარიელ 500-ს აბრუნებს. ჩართე სტანდარტული შეცდომის ფორმატი:

csharp
builder.Services.AddProblemDetails();var app = builder.Build();app.UseExceptionHandler();app.UseStatusCodePages();

ახლა დაუმუშავებელი გამონაკლისები application/problem+json პასუხს აწარმოებს სტატუსითა და სათაურით, body-ის გარეშე სტატუს კოდებიც (404 routing-იდან, 401 ავთენტიფიკაციიდან) იღებენ მას, და ფორმატი ზემოთ აღწერილ ვალიდაციის შეცდომებს ემთხვევა. Development-ში პასუხი გამონაკლისის დეტალებს შეიცავს; Production-ში - არა, რაც სწორედ ის არის, რაც გინდა. გამონაკლისი სერვერზე ჩაწერე ლოგში, კლიენტს დაუბრუნე correlation იდენტიფიკატორი, თუ მოგვიანებით მისი პოვნა დაგჭირდება, და შენი მონაცემთა ბაზის დრაივერის გამონაკლისის შეტყობინებები პასუხში არასოდეს ჩასვა.

მოსალოდნელი ჩავარდნებისთვის - არარსებული ჩანაწერი, კონფლიქტი, ბიზნეს წესი - შედეგი აშკარად დააბრუნე (TypedResults.NotFound(), TypedResults.Conflict(), TypedResults.Problem(...)) და ნუ ისვრი გამონაკლისს. გამონაკლისები ძვირია და კონტროლის ნაკადს ძნელად გასაგებს ხდის; union დასაბრუნებელი ტიპი მოსალოდნელ შედეგებს ხილულს ხდის.

ხშირი შუალედური გზაა საკუთარი exception handler რამდენიმე ცნობილი გამონაკლისის ტიპისთვის - მაგალითად, EF Core-ის concurrency კონფლიქტი, რომელიც 409-ად იქცევა. .NET 8-იდან შეგიძლია IExceptionHandler-ის იმპლემენტაცია, მისი რეგისტრაცია AddExceptionHandler<T>()-ით და ნება დართო, problem details პასუხი დაწეროს იმ გამონაკლისებისთვის, რომლებსაც ცნობს, დანარჩენი კი ნაგულისხმევ ქცევას გადასცეს.

OpenAPI დოკუმენტაცია#

.NET 9-იდან ASP.NET Core OpenAPI დოკუმენტებს თავად აგენერირებს Microsoft.AspNetCore.OpenApi პაკეტით, და შაბლონებში Swashbuckle აღარ შედის.

csharp
builder.Services.AddOpenApi();var app = builder.Build();app.MapOpenApi();   // serves /openapi/v1.json

.NET 10 ნაგულისხმევად OpenAPI 3.1-ს აგენერირებს. endpoint-ები მათი route-ით, პარამეტრებითა და დასაბრუნებელი ტიპებით აღიწერება; დაამატე .WithName("GetOrder"), .WithSummary(...) და .WithTags(...) იმ ნაწილებისთვის, რასაც ხელმოწერა ვერ ამბობს. TypedResults union დასაბრუნებელი ტიპებით ყოველ პასუხს ავტომატურად აღწერს, რაც მათ სასარგებლოდ ყველაზე ძლიერი არგუმენტია.

პაკეტი დოკუმენტს აწარმოებს და არა მის სანახავ ინტერფეისს. ინტერაქტიული გვერდისთვის დაამატე ერთ-ერთი: Scalar (Scalar.AspNetCore, შემდეგ app.MapScalarApiReference()) ან Swagger UI Swashbuckle.AspNetCore.SwaggerUI პაკეტიდან, მიმართული /openapi/v1.json-ზე. გადაწყვიტე, უნდა იყოს თუ არა დოკუმენტი და ინტერფეისი საჯარო. შიდა API-სთვის დააკავშირე ისინი მხოლოდ Development-ში ან დამალე RequireAuthorization()-ის უკან; ყოველი endpoint-ის საჯარო აღწერა საჩუქარია ყველასთვის, ვინც შენს API-ს ჩხრეკს.

ფილტრები, ავტორიზაცია და rate limit-ები#

endpoint ფილტრები handler-ის გარშემო ეშვება და მის არგუმენტებსა და შედეგზე წვდომა აქვს:

csharp
group.AddEndpointFilter(async (context, next) =>{    var started = Stopwatch.GetTimestamp();    var result = await next(context);    var ms = Stopwatch.GetElapsedTime(started).TotalMilliseconds;    context.HttpContext.Response.Headers["Server-Timing"] = $"app;dur={ms:F0}";    return result;});

ავტორიზაცია და rate limiting იმავე გზით მიება. RequireAuthorization() არგუმენტის გარეშე ავთენტიფიცირებულ მომხმარებელს მოითხოვს; პოლიტიკის სახელით ის ამ პოლიტიკას იყენებს. RequireRateLimiting("per-ip") იყენებს AddRateLimiter-ით დარეგისტრირებულ პოლიტიკას. IP-ით გასაღებულ rate limit-ს კლიენტის რეალური მისამართი სჭირდება, რაც proxy-ს უკან სწორად დაკონფიგურირებულ forwarded header-ებს ნიშნავს - ASP.NET Core reverse proxy-ს უკან აჩვენებს, როგორ, ხოლო ASP.NET Core-ის ავთენტიფიკაციის საფუძვლები ავთენტიფიკაციის მხრივ cookie-ებსა და JWT-ს ადარებს.

თუ API-ს სხვა origin-ზე მდებარე ბრაუზერის აპლიკაცია იძახებს, მას CORS სჭირდება. დაარეგისტრირე დასახელებული პოლიტიკა ზუსტად დაშვებული origin-ებით (AddCors WithOrigins("https://app.example.com")-ით), დაამატე UseCors() pipeline-ში ავტორიზაციამდე და group-ზე პოლიტიკა გამოიყენე RequireCors("app")-ით. ნუ შეუთავსებ AllowAnyOrigin()-ს credentials-ს - ბრაუზერები ამ წყვილს უარყოფენ, და შეცდომა მხოლოდ ბრაუზერის კონსოლში ჩანს და არასოდეს შენი სერვერის ლოგში. preflight OPTIONS მოთხოვნებს თავად CORS middleware პასუხობს, ამიტომ ისინი შენს handler-ებს არასოდეს აღწევენ და საკუთარი route არასოდეს სჭირდებათ.

როდის გამოვიყენოთ controller-ები#

Minimal API-ები და controller-ები ერთსა და იმავე pipeline-ზე მუშაობს, და ერთ აპლიკაციას ორივე შეიძლება ჰქონდეს (AddControllers() პლუს MapControllers()). მიზეზები, რომ აპლიკაციის ნაწილისთვის ან მთლიანად controller-ები აირჩიო:

  • დიდი არსებული კოდის ბაზა ან გუნდის ჩვევა. მომუშავე controller-ების გადაწერა ცოტას გაძლევს.
  • Content negotiation. controller-ებს formatter-ებით XML-ის ან სხვა ფორმატების დაბრუნება შეუძლიათ; minimal API-ები პირველ რიგში JSON-ზეა ორიენტირებული.
  • MVC ფილტრების ეკოსისტემები. action ფილტრები, result ფილტრები და model binder-ები ბიბლიოთეკებიდან, რომლებზეც უკვე ხარ დამოკიდებული.
  • OData, რომელიც ისტორიულად controller-ების გარშემოა აგებული.
  • კონვენციებზე დაფუძნებული API-ები, სადაც attribute routing და საბაზო controller-იდან მემკვიდრეობა მეტ გამეორებას აშორებს, ვიდრე route group-ები შეძლებდნენ.

წარმადობა არცერთის სასარგებლოდ ძლიერი მიზეზი არ არის. Minimal API-ები მოთხოვნაზე ნაკლებ სამუშაოს აკეთებენ და ნაკლებს ალოკირებენ, და ისინი Native AOT-მდე მისასვლელი გზაა, მაგრამ API-სთვის, რომელიც დროს მონაცემთა ბაზაში ატარებს, განსხვავება არ გამოჩნდება. dotnet publish-ის პარამეტრები AOT-ს განიხილავს, თუ გაშვების დრო და მეხსიერება შენთვის მნიშვნელოვანია.

ორს შორის გადასვლა ერთბაშად არ უნდა მოხდეს. არსებულ controller-ებზე დაფუძნებულ API-ს შეუძლია ახალი ფუნქციონალი minimal API group-ებად დაამატოს, სანამ ძველი controller-ები აგრძელებენ მუშაობას, და პირიქითაც ისევე შესაძლებელია. routing ცხრილი საერთოა, ამიტომ ერთადერთი წესი ის არის, რომ ორმა endpoint-მა ერთი და იგივე route და მეთოდი არ უნდა დაიჩემოს; თუ დაიჩემეს, მოთხოვნა ვარდება ორაზროვანი დამთხვევის შეცდომით, რომელიც ორივეს ასახელებს და რომლის გასწორებაც ადვილია. გუნდები, რომლებიც ასე მიგრირებენ, ჩვეულებრივ ერთ ფუნქციონალს ერთ ჯერზე გარდაქმნიან, უმარტივესიდან დაწყებული, და ჩერდებიან, როცა დარჩენილი controller-ები ძალისხმევად აღარ ღირს.

minimal API-ის deploy ნებისმიერი ASP.NET Core აპლიკაციის deploy-ის მსგავსია: publish, Kestrel-ის მიბმა გამოყოფილ პორტზე ყველა ინტერფეისზე და დომენის proxy-ზე განთავსება. ASP.NET Core აპლიკაციის deploy GitHub-იდან სრულ თანმიმდევრობას შეიცავს, ხოლო EF Core-ის მიგრაციები production-ში მონაცემთა ბაზის სქემას განიხილავს, რომელზეც API-ების უმეტესობა დგას.

FAQ#

Minimal API-ები მხოლოდ პატარა პროექტებისთვისაა?

არა. სახელი ცერემონიის რაოდენობას აღწერს და არა აპლიკაციის ზომას. route group-ებით და თითო ფუნქციონალზე ერთი ფაილით minimal API ასობით endpoint-ზეც მოწესრიგებული რჩება.

შემიძლია dependency injection-ის გამოყენება minimal API handler-ებში?

კი. ნებისმიერი პარამეტრი, რომლის ტიპიც კონტეინერშია რეგისტრირებული, ყოველ მოთხოვნაზე იხსნება, scoped სერვისების ჩათვლით. კონსტრუქტორი არ არის საჭირო.

რატომ არის ჩემი მოთხოვნის body ყოველთვის null?

JSON პარამეტრის ტიპს არ ემთხვევა, Content-Type header არ არის application/json, ან მეთოდი GET-ია, რომელიც ნაგულისხმევად body-ს არ აბამს. შეამოწმე მოთხოვნა ლოგში და შეადარე property-ების სახელები; binding რეგისტრს არ ითვალისწინებს, მაგრამ მართლწერას მაინც აქვს მნიშვნელობა.

ჯერ კიდევ მჭირდება Swashbuckle?

დოკუმენტის დასაგენერირებლად - არა, ამას Microsoft.AspNetCore.OpenApi .NET 9-იდან აკეთებს. UI პაკეტი მხოლოდ მაშინ გჭირდება, თუ ინტერაქტიული გვერდი გინდა, და Scalar-იც და Swagger UI პაკეტიც ჩაშენებულ დოკუმენტთან მუშაობს.

როგორ გავუკეთო ვერსიონირება minimal API-ს?

ყველაზე მარტივი გზაა თითო ვერსიაზე route group, /api/v1 და /api/v2, რომელთაგან თითოეული საკუთარ endpoint-ებს აკავშირებს. Asp.Versioning.Http პაკეტი header-სა და query-ზე დაფუძნებულ ვერსიონირებას ამატებს, თუ ეს გჭირდება.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000