- Web API layihəsi yaratmaq və
Program.cs-in quruluşunu (xidmətlər, middleware, endpoint-lər) izah etmək - EF Core ilə minimal API və kontroller üslubunda CRUD endpoint-ləri yazmaq
- DI ömürlərini seçmək, konfiqurasiyanı oxumaq və API-ni curl və OpenAPI ilə yoxlamaq
Əvvəlki dərsdə verilənləri EF Core ilə bazada saxlamağı öyrəndin. İndi onları dünyaya açaq: mobil tətbiq, veb-sayt və ya başqa server HTTP vasitəsilə şagirdlərin siyahısını istəyə və yeni şagird əlavə edə bilsin. ASP.NET Core — .NET-in sürətli, çoxplatformalı və açıq mənbəli veb freymvorkudur. Bu dərsdə onunla EF Core üzərində tam CRUD API quracaq, asılılıq inyeksiyasını, middleware-i, konfiqurasiyanı və OpenAPI sənədini öyrənəcəksən.
- 1Layihəni yarat
dotnet new webapi -n SchoolApisusmaya görə minimal API yaradır; kontrollerlər üçün--use-controllersəlavə et. - 2EF Core-u qoş
Microsoft.EntityFrameworkCore.Sqlitepaketini əlavə et, model və kontekst siniflərini yaz. - 3Konfiqurasiya və baza
Qoşulma sətrini
appsettings.json-a yaz, sonra əvvəlki dərsdəki kimidotnet ef migrations addvədotnet ef database updateicra et. - 4İşə sal və yoxla
dotnet run --urls http://localhost:5080serveri başladır; sorğuları curl,.httpfaylı və ya Swagger UI ilə göndər.
dotnet new webapi -n SchoolApi
cd SchoolApi
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet run --urls http://localhost:5080info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5080
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.Program.cs: xidmətlər və middleware
Minimal API-də bütün tətbiq Program.cs-də qurulur və üç hissədən ibarətdir. Əvvəlcə builder.Services-də xidmətlər qeydə alınır — bu, .NET-in daxili asılılıq inyeksiyası (DI) konteyneridir: AddDbContext SchoolContext-i, AddOpenApi sənəd generatorunu, AddValidation isə atributlara əsaslanan yoxlamanı əlavə edir. Sonra builder.Build() tətbiqi yaradır və middleware konveyeri qurulur. Axırda endpoint-lər — URL-lərə bağlanmış metodlar — gəlir.
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
// 1. services for dependency injection
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("School")));
builder.Services.AddOpenApi();
builder.Services.AddValidation(); // .NET 10: checks [Required], [Range]...
var app = builder.Build();
// 2. the middleware pipeline
if (app.Environment.IsDevelopment())
app.MapOpenApi(); // the document at /openapi/v1.json
app.Use(async (context, next) => // our own middleware: request timing
{
var watch = System.Diagnostics.Stopwatch.StartNew();
await next(context);
app.Logger.LogInformation("{Method} {Path} -> {Status} in {Ms} ms", context.Request.Method,
context.Request.Path, context.Response.StatusCode, watch.ElapsedMilliseconds);
});Program.cs, 1-ci hissə: xidmətlər və middleware (EF Core və OpenAPI paketləri tələb olunur)Middleware hər HTTP sorğusunun keçdiyi zəncirin həlqəsidir. Hər həlqə sorğuya baxa, onu dəyişə, cavabı özü verə və ya next-i çağırıb sorğunu növbəti həlqəyə ötürə bilər. Yuxarıdakı öz middleware-imiz saatı işə salır, await next(context) ilə zəncirin qalanını gözləyir, sonra isə statusu və müddəti jurnala yazır. Həlqələrin sırası vacibdir və adətən belədir:
UseExceptionHandler— aşağıdakı bütün həlqələrdən gələn xətaları tuturUseHttpsRedirection— HTTP sorğularını HTTPS-ə yönləndirirUseCors— hansı saytların API-ni çağıra biləcəyini müəyyən edirUseAuthentication— «sən kimsən?»UseAuthorization— «sənə nə icazə verilib?»- Endpoint-lər:
MapGet,MapPost,MapControllers
EF Core ilə CRUD endpoint-ləri
public class Student
{
public int Id { get; set; }
public required string Name { get; set; }
public int Score { get; set; }
}
public class SchoolContext(DbContextOptions<SchoolContext> options) : DbContext(options)
{
public DbSet<Student> Students => Set<Student>();
}
public record StudentDto([Required] string Name, [Range(0, 100)] int Score);// 3. endpoints
var students = app.MapGroup("/api/students");
students.MapGet("/", async (SchoolContext db) =>
await db.Students.AsNoTracking().OrderBy(s => s.Id).ToListAsync());
students.MapGet("/{id:int}", async Task<Results<Ok<Student>, NotFound>> (int id, SchoolContext db) =>
await db.Students.FindAsync(id) is Student s ? TypedResults.Ok(s) : TypedResults.NotFound());
students.MapPost("/", async (StudentDto dto, SchoolContext db) =>
{
var student = new Student { Name = dto.Name, Score = dto.Score };
db.Students.Add(student);
await db.SaveChangesAsync();
return TypedResults.Created($"/api/students/{student.Id}", student);
});
students.MapPut("/{id:int}", async Task<Results<NoContent, NotFound>> (int id, StudentDto dto, SchoolContext db) =>
await db.Students.Where(s => s.Id == id)
.ExecuteUpdateAsync(set => set.SetProperty(s => s.Name, dto.Name).SetProperty(s => s.Score, dto.Score)) == 0
? TypedResults.NotFound() : TypedResults.NoContent());
students.MapDelete("/{id:int}", async Task<Results<NoContent, NotFound>> (int id, SchoolContext db) =>
await db.Students.Where(s => s.Id == id).ExecuteDeleteAsync() == 0
? TypedResults.NotFound() : TypedResults.NoContent());
app.Run();Program.cs, 2-ci hissə: beş endpoint — oxumaq, yaratmaq, yeniləmək, silməkMapGroup ümumi /api/students prefiksini bir dəfə yazmağa imkan verir. Endpoint-in parametrləri avtomatik doldurulur: id URL-dən ({id:int} yalnız tam ədəd qəbul edir), StudentDto JSON gövdəsindən, SchoolContext isə DI konteynerindən. TypedResults düzgün statusları qaytarır — Created 201 və Location başlığı, NoContent 204, NotFound 404; Results<...> tipi isə bütün mümkün cavabları sənədləşdirir və OpenAPI də onları görür. JSON-da adlar avtomatik camelCase olur: Name → name.
{
"ConnectionStrings": {
"School": "Data Source=school.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}appsettings.json: GetConnectionString("School") bu qiyməti oxuyur; appsettings.Development.json və mühit dəyişənləri onu əvəz edə bilərcurl ilə yoxlama
curl -X POST localhost:5080/api/students -H 'Content-Type: application/json' -d '{"name":"Aysel","score":95}'
curl -X POST localhost:5080/api/students -H 'Content-Type: application/json' -d '{"name":"Murad","score":78}'
curl localhost:5080/api/students
curl -s -o /dev/null -w '%{http_code}\n' -X PUT localhost:5080/api/students/2 \
-H 'Content-Type: application/json' -d '{"name":"Murad","score":84}'
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE localhost:5080/api/students/2
curl -s -o /dev/null -w '%{http_code}\n' localhost:5080/api/students/2
curl -X POST localhost:5080/api/students -H 'Content-Type: application/json' -d '{"name":"Elvin","score":120}'{"id":1,"name":"Aysel","score":95}
{"id":2,"name":"Murad","score":78}
[{"id":1,"name":"Aysel","score":95},{"id":2,"name":"Murad","score":78}]
204
204
404
{"title":"One or more validation errors occurred.","errors":{"Score":["The field Score must be between 0 and 100."]}}Kontrollerlər, DI ömürləri və OpenAPI
Minimal API kiçik və orta servislər üçün rahatdır. Böyük layihələrdə çox vaxt kontrollerlər işlədilir: endpoint-lər ControllerBase-dən törəyən siniflərdə qruplaşır, marşrutlar isə atributlarla yazılır. dotnet new webapi --use-controllers belə layihə yaradır. Hər iki üslub eyni DI-dən, middleware-dən və EF Core-dan istifadə edir, onları bir layihədə birləşdirmək də olar.
[ApiController]
[Route("api/[controller]")] // -> /api/students
public class StudentsController(SchoolContext db) : ControllerBase
{
[HttpGet]
public async Task<List<Student>> GetAll() =>
await db.Students.AsNoTracking().OrderBy(s => s.Id).ToListAsync();
[HttpGet("{id:int}")]
public async Task<ActionResult<Student>> Get(int id) =>
await db.Students.FindAsync(id) is Student s ? s : NotFound();
[HttpPost]
public async Task<ActionResult<Student>> Create(StudentDto dto)
{
var student = new Student { Name = dto.Name, Score = dto.Score };
db.Students.Add(student);
await db.SaveChangesAsync();
return CreatedAtAction(nameof(Get), new { id = student.Id }, student);
}
}
// Program.cs: builder.Services.AddControllers(); ... app.MapControllers();SchoolContext əsas konstruktordan gəlir, [ApiController] isə yoxlama xətalarında avtomatik 400 qaytarır| Metod | Ömür | Nə üçün |
|---|---|---|
AddSingleton | bütün tətbiq üçün bir nüsxə | konfiqurasiya, yaddaşdakı keş |
AddScoped | hər HTTP sorğusu üçün bir nüsxə | DbContext və onu işlədən servislər |
AddTransient | hər istəkdə yeni nüsxə | yüngül, vəziyyətsiz köməkçilər |
builder.Services.AddDbContext<SchoolContext>(...); // scoped: one per request
builder.Services.AddSingleton<ReportService>(); // ReportService needs SchoolContext
// error at startup (Development):
// Cannot consume scoped service '...SchoolContext' from singleton '...ReportService'.builder.Services.AddDbContext<SchoolContext>(...); // scoped: one per request
builder.Services.AddScoped<ReportService>(); // the same lifetime as its dependencyDbContext bütün sorğular arasında bölüşdürülərdiOpenAPI (keçmiş adı Swagger) API-nin maşın oxuya bilən təsviridir: hansı URL-lər var, hansı JSON gözlənilir, hansı statuslar qaytarılır. .NET 9-dan bəri şablon AddOpenApi() və MapOpenApi() işlədir, sənəd isə /openapi/v1.json ünvanında yaranır. Brauzerdə interaktiv səhifə üçün Swashbuckle.AspNetCore.SwaggerUI paketini əlavə et və app.UseSwaggerUI(o => o.SwaggerEndpoint("/openapi/v1.json", "School API")) yaz: /swagger səhifəsində hər endpoint-i düymə ilə sınaya bilərsən. Mobil tətbiqlər üçün müştəri kodu da bu sənəddən avtomatik yaradıla bilər.
Əsas fikirlər
Program.cs: xidmətlər (DI) →Build()→ middleware → endpoint-lər →Run().- Minimal API:
MapGroup,MapGet/MapPost/MapPut/MapDelete; parametrlər URL-dən, gövdədən və DI-dən gəlir,TypedResults200/201/204/404 qaytarır. - DI ömürləri:
Singleton,Scoped(DbContext),Transient; scoped xidməti singleton-a vermə. - Konfiqurasiya
appsettings.json, mühitə görə fayllar və mühit dəyişənlərindən gəlir; sətriGetConnectionStringoxuyur. - Girişdə DTO və
AddValidation; OpenAPI sənədi/openapi/v1.json-dadır, Swagger UI paketlə əlavə olunur.
Özünü yoxla
10 sual. Hər düzgün cavab XP qazandırır.
AddDbContext DbContext-i hansı ömürlə qeydə alır?