Məzmuna keç
Educora
İrəli25 dəq16 / 16

ASP.NET Core ilə Web API

ASP.NET Core ilə EF Core üzərində CRUD API qur: minimal API və kontrollerlər, asılılıq inyeksiyası, middleware, konfiqurasiya, yoxlama və OpenAPI.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • 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.

  1. 1
    Layihəni yarat

    dotnet new webapi -n SchoolApi susmaya görə minimal API yaradır; kontrollerlər üçün --use-controllers əlavə et.

  2. 2
    EF Core-u qoş

    Microsoft.EntityFrameworkCore.Sqlite paketini əlavə et, model və kontekst siniflərini yaz.

  3. 3
    Konfiqurasiya və baza

    Qoşulma sətrini appsettings.json-a yaz, sonra əvvəlki dərsdəki kimi dotnet ef migrations add və dotnet ef database update icra et.

  4. 4
    İşə sal və yoxla

    dotnet run --urls http://localhost:5080 serveri başladır; sorğuları curl, .http faylı və ya Swagger UI ilə göndər.

Terminal
dotnet new webapi -n SchoolApi
cd SchoolApi
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet run --urls http://localhost:5080
Gözlənilən nəticə
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
Nümunə nəticə (qısaldılıb): daxili Kestrel serveri 5080 portunu dinləyir

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.

C#
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:

  1. UseExceptionHandler — aşağıdakı bütün həlqələrdən gələn xətaları tutur
  2. UseHttpsRedirection — HTTP sorğularını HTTPS-ə yönləndirir
  3. UseCors — hansı saytların API-ni çağıra biləcəyini müəyyən edir
  4. UseAuthentication — «sən kimsən?»
  5. UseAuthorization — «sənə nə icazə verilib?»
  6. Endpoint-lər: MapGet, MapPost, MapControllers

EF Core ilə CRUD endpoint-ləri

C#
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);
Sadələşdirilmiş model, parametrləri DI-dən alan kontekst və giriş üçün DTO
C#
// 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ək

MapGroup ü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.

JSON
{
  "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ər

curl ilə yoxlama

Terminal
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}'
Gözlənilən nəticə
{"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."]}}
Nəticə eyni API-nin verilənləri yaddaşda saxlayan versiyası ilə .NET 10-da yoxlanılıb; boş SQLite bazası ilə cavablar eynidir. Sonuncu sorğu yoxlamadan keçmədi və 400 aldı

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.

C#
[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();
Eyni API kontroller ilə: SchoolContext əsas konstruktordan gəlir, [ApiController] isə yoxlama xətalarında avtomatik 400 qaytarır
MetodÖmürNə üçün
AddSingletonbütün tətbiq üçün bir nüsxəkonfiqurasiya, yaddaşdakı keş
AddScopedhər HTTP sorğusu üçün bir nüsxəDbContext və onu işlədən servislər
AddTransienthər istəkdə yeni nüsxəyüngül, vəziyyətsiz köməkçilər
Səhv: singleton scoped xidməti tutur
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'.
Düzgün: uyğun ömürlər
builder.Services.AddDbContext<SchoolContext>(...);   // scoped: one per request
builder.Services.AddScoped<ReportService>();         // the same lifetime as its dependency
Development rejimində ASP.NET Core bu səhvi hələ işə salarkən tapır; əks halda bir DbContext bütün sorğular arasında bölüşdürülərdi

OpenAPI (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, TypedResults 200/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ətri GetConnectionString oxuyur.
  • 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.

1 / 10
AddDbContext DbContext-i hansı ömürlə qeydə alır?