- Bir Web API projesi oluşturmak ve
Program.csdosyasının yapısını (servisler, ara katman, uç noktalar) açıklamak - EF Core ile hem minimal API hem de denetleyici tarzında CRUD uç noktaları yazmak
- DI yaşam sürelerini seçmek, yapılandırmayı okumak ve API'yi curl ve OpenAPI ile test etmek
Önceki derste verileri EF Core ile veritabanında saklamayı öğrendin. Şimdi onları dünyaya açalım: bir mobil uygulama, bir web sitesi ya da başka bir sunucu, HTTP üzerinden öğrenci listesini isteyebilmeli ve yeni öğrenci ekleyebilmelidir. ASP.NET Core, .NET'in hızlı, çok platformlu ve açık kaynaklı web çatısıdır. Bu derste onunla EF Core üzerinde tam bir CRUD API kuracak; bağımlılık enjeksiyonunu, ara katmanı (middleware), yapılandırmayı ve OpenAPI belgesini öğreneceksin.
- 1Projeyi oluştur
dotnet new webapi -n SchoolApivarsayılan olarak bir minimal API oluşturur; denetleyiciler için--use-controllersekle. - 2EF Core'u ekle
Microsoft.EntityFrameworkCore.Sqlitepaketini ekle, model ve bağlam sınıflarını yaz. - 3Yapılandırma ve veritabanı
Bağlantı dizesini
appsettings.jsondosyasına yaz, ardından önceki dersteki gibidotnet ef migrations addvedotnet ef database updateçalıştır. - 4Çalıştır ve test et
dotnet run --urls http://localhost:5080sunucuyu başlatır; istekleri curl, bir.httpdosyası ya da Swagger UI ile gönder.
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: servisler ve ara katman
Bir minimal API'de tüm uygulama Program.cs içinde kurulur ve üç bölümden oluşur. Önce builder.Services içine servisler kaydedilir; bu, .NET'in yerleşik bağımlılık enjeksiyonu (DI) kapsayıcısıdır: AddDbContext, SchoolContext'i; AddOpenApi, belge oluşturucuyu; AddValidation ise özniteliklere dayalı doğrulamayı ekler. Ardından builder.Build() uygulamayı oluşturur ve ara katman (middleware) hattı kurulur. En sonda uç noktalar, yani URL'lere bağlanmış metotlar gelir.
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. bölüm: servisler ve ara katman (EF Core ve OpenAPI paketlerini gerektirir)Ara katman (middleware), her HTTP isteğinin geçtiği zincirin bir halkasıdır. Her halka isteğe bakabilir, onu değiştirebilir, yanıtı kendisi verebilir ya da next'i çağırıp isteği bir sonraki halkaya iletebilir. Yukarıdaki kendi ara katmanımız bir kronometre başlatır, await next(context) ile zincirin geri kalanını bekler, ardından durum kodunu ve süreyi günlüğe yazar. Halkaların sırası önemlidir ve genellikle şöyledir:
UseExceptionHandler— altındaki tüm halkalardan gelen hataları yakalarUseHttpsRedirection— HTTP isteklerini HTTPS'e yönlendirirUseCors— hangi sitelerin API'yi çağırabileceğini belirlerUseAuthentication— “sen kimsin?”UseAuthorization— “neye izin verildi?”- Uç noktalar:
MapGet,MapPost,MapControllers
EF Core ile CRUD uç noktaları
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. bölüm: beş uç nokta — okuma, oluşturma, güncelleme, silmeMapGroup, ortak /api/students önekini bir kez yazmanı sağlar. Bir uç noktanın parametreleri otomatik olarak doldurulur: id URL'den ({id:int} yalnızca tam sayı kabul eder), StudentDto JSON gövdesinden, SchoolContext ise DI kapsayıcısından. TypedResults doğru durum kodlarını döndürür: Created 201 ve bir Location başlığı, NoContent 204, NotFound 404 verir; Results<...> türü ise tüm olası yanıtları belgeler, OpenAPI da onları görür. JSON'daki adlar otomatik olarak camelCase olur: Name → name.
{
"ConnectionStrings": {
"School": "Data Source=school.db"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}appsettings.json: GetConnectionString("School") bu değeri okur; appsettings.Development.json ve ortam değişkenleri onu geçersiz kılabilircurl ile test
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."]}}Denetleyiciler, DI yaşam süreleri ve OpenAPI
Minimal API'ler küçük ve orta ölçekli servisler için kullanışlıdır. Büyük projelerde ise çoğu zaman denetleyiciler (controllers) kullanılır: uç noktalar ControllerBase'ten türeyen sınıflarda gruplanır, yollar özniteliklerle yazılır. dotnet new webapi --use-controllers böyle bir proje oluşturur. İki tarz da aynı DI'ı, ara katmanı ve EF Core'u kullanır; hatta tek bir projede birleştirilebilirler.
[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 birincil yapıcıdan gelir, [ApiController] ise doğrulama hatalarında otomatik olarak 400 döndürür| Metot | Yaşam süresi | Ne için |
|---|---|---|
AddSingleton | tüm uygulama için tek örnek | yapılandırma, bellek içi önbellek |
AddScoped | her HTTP isteği için bir örnek | DbContext ve onu kullanan servisler |
AddTransient | her istendiğinde yeni bir örnek | hafif, durumsuz yardımcılar |
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 tüm istekler arasında paylaşılırdıOpenAPI (eski adıyla Swagger), bir API'nin makinece okunabilir tanımıdır: hangi URL'ler var, hangi JSON'u bekliyorlar ve hangi durum kodlarını döndürüyorlar. .NET 9'dan beri şablon AddOpenApi() ve MapOpenApi() kullanır; belge /openapi/v1.json adresinde sunulur. Tarayıcıda etkileşimli bir sayfa için Swashbuckle.AspNetCore.SwaggerUI paketini ekle ve app.UseSwaggerUI(o => o.SwaggerEndpoint("/openapi/v1.json", "School API")) yaz: /swagger sayfasında her uç noktayı bir düğmeyle deneyebilirsin. Mobil uygulamalar için istemci kodu da bu belgeden otomatik olarak üretilebilir.
Önemli noktalar
Program.cs: servisler (DI) →Build()→ ara katman → uç noktalar →Run().- Minimal API:
MapGroup,MapGet/MapPost/MapPut/MapDelete; parametreler URL'den, gövdeden ve DI'dan gelir,TypedResults200/201/204/404 döndürür. - DI yaşam süreleri:
Singleton,Scoped(DbContext),Transient; scoped bir servisi asla bir singleton'a verme. - Yapılandırma
appsettings.json, ortama özel dosyalar ve ortam değişkenlerinden gelir; bağlantı dizesiniGetConnectionStringokur. - Girdide DTO ve
AddValidation; OpenAPI belgesi/openapi/v1.jsonadresindedir, Swagger UI ise bir paketle eklenir.
Kendini test et
10 soru. Her doğru cevap XP kazandırır.
AddDbContext, DbContext'i hangi yaşam süresiyle kaydeder?