Swagger’ın değerini sadece kullananlar bilir, developer dostudur swagger, yarı yolda bırakmaz. Müşteriye & kullanıcıya ulaşmadan 1 tıkla test ettirir endpointleri, kabul edilen json yapısını hazır bekletir. Sen sadece TRY’a tıklarsın sonucu görürsün. Bazen crash olur hatanı görürsün bazen success olur mutlu olursun. Dürüst adamdır neyse sonuç onunla yüzleştirir seni…
Bu yazımızın konusu ASP.NET CORE projesi için swagger entegrasyonu. Bildiğin gibi swagger artık direk kurulu gelmiyor ama hala iki tık uzağımızda.
Nedir bu swagger?
Swagger bir api dökümantasyon ve endpoint test aracıdır. Bir web api geliştirdiğinde onu otomatik olarak dökümante eder, ve sana tüm endpointlerin bir listesini çıkartır. Swagger bir OpenAPI Specification (OAS) standartıdır. Test arayüzü sunar, annotation ve metod isimlerinden ve parametlererinden otomatik girdileri field olarak karşına çıkartır.
Otomatik kodları tarar ve endpointleri oluşturur.
Test arayüzü sunar.
Authorize butonuna tıklayarak JWT girebilirsin ve bu sayede korumalı endpoinleri de test edebilirsin.
ASP.NET Core ‘da Swagger kuralım (.net 9 ve üzeri)
Öncelikle paketlerimizi ekleyelim projemize, arayüz kullananlar direk nuget üzerinden de yapabilir.
PowerShell
dotnet add package Swashbuckle.AspNetCore
Bu kodu proje dizininde çalıştırırsan swagger projene konuk oyuncu olarak katılır. Öyle misafir gibi de davranmaz hemen birkaç komutla işe koyulur…
Paketimizi kurduktan sonra tek bir dosya üzerinden development ortamımıza dahil edebiliriz swagger’ı adım adım anlatacağım ve en sonda Program.cs’ın son halini vereceğim.
Ekleyeceğimiz kod satırları sırayla şu şekilde.
Program.cs Son hali
C#
var builder =WebApplication.CreateBuilder(args);// Add services to the container.builder.Services.AddControllers();// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapibuilder.Services.AddOpenApi();// Swagger servisini eklebuilder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();var app =builder.Build();// Configure the HTTP request pipeline.if(app.Environment.IsDevelopment()){app.MapOpenApi();app.UseSwagger();app.UseSwaggerUI(options =>{options.SwaggerEndpoint("/swagger/v1/swagger.json","ECommerce API V1");options.RoutePrefix=string.Empty;});// set options to root directory of api}app.UseHttpsRedirection();app.UseAuthorization();app.MapControllers();app.Run();
Şimdi adım adım nasıl entegre ettiğimizi anlatalım, entegrasyonumuz iki adımdan oluşuyor öncelikle dependency injection kısmı sonrasında ise option ayarları.
“builder.Services.AddOpenApi();” satırından sonra ve “var app = builder.Build();” satırından önce swagger servislerini eklemiş olduk.
C#
builder.Services.AddOpenApi();// bu satırdan sonra// Swagger servisini eklebuilder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();var app =builder.Build();// bu satırdan önce
sonrasında sadece development mode da çalışması için:
C#
// Configure the HTTP request pipeline.if(app.Environment.IsDevelopment()){app.MapOpenApi();}
bu if blogu içeriğini şu şekilde düzenledik:
C#
// Configure the HTTP request pipeline.if(app.Environment.IsDevelopment()){app.MapOpenApi();app.UseSwagger();app.UseSwaggerUI(options =>{options.SwaggerEndpoint("/swagger/v1/swagger.json","ECommerce API V1");options.RoutePrefix=string.Empty;});// set options to root directory of api}
Burada swagger’ın gerekli option’larını ve ana dizinde açılması için gerekli ayarlarını yapmış olduk.
sonrasında projemizi çalıştırdığımızda ana dizine gittiğimizde direk swagger gökümantasyonu bizi karşılıyor:
Bir sonraki yazımızda swagger ile JWT authorized api testi sizlerle olacak.
Her yeni SQL Server + EF Core projesi oluştururken tekrar – tekarar connection string oluşturmam gerekiyor ve bu gerçekten sıkıcı bir hal aldı, bu yazıca Connection String nasıl oluşturulur ve proje içinde appsettings.json içindeki bu connection string ve diğer değerlere nasıl erişilir bundan kendi dilimce bahsedeceğim ve ihtiyaç oldukça ben de sen de bu kaynağı takip ederek kullanabiliriz.
Connection String Nedir?
Connection String uygulamaların veritabanına nasıl bağlanacağı bilgisini saklayan bir metin değeridir aslında. İçerisinde Server, veritabanı adı, şifre, gibi bilgiler barındırır.
İçeriği şu bileşenlerden oluşabilir:
Server(Sunucu adresimiz)
Database(Veritabanı adımız)
Trusted_Connection(Windows oturumu ile bağlan:Şifre gerektirmez)
Burada “Server Name” yazan yerde hali hazırda conneciton string için “Server” Kısmı bulunmaktadır ve gördüğünüz gibi Authenticationkısmı da “Windows Authentication” yani şifre gerektirmeyen bir yöntemdir.
2.Veritabanı Adı
Veritabanı adına da listelenen veritabanları üzerinden ulaşabilirsiniz. Aynı şekilde veritabanına sağ tıklayarak Properties > View Connection Properties bağlantısı üzerinden de veritabanı adına ve server adresine ulaşabilirsiniz.
Ekranda Server Name ve Database bilgileri yer alıyor. bunları kullanarak connection string deki istediğiniz alanları düzenleyebilirsiniz.
Örnek Kullanıcı Adı ve Şifre Girişi için Connection String
Eğer veritabanınızı özel kullanıcı adı ve şifre ile oluşturduysanız örnek connection string şu şekilde olacak
Yeni bir asp.net core projesi oluşturduğunuzda büyük ihtimalle ilk yapmanız gerekenlerden biri “EF configuration” ile veritabanı bağlantısı kurmak ve modellerinizi EF’e tanıtmak olacaktır.
Bu yazıda sıfırdan bir projede basitçe model oluşturup bir api endpoint üzerinden EF ile MSSQL veritabanına bağlanarak veri çekip kullanıcının tarayıcısına ileteceğiz. Sadece EF kısmına odaklanacağız ve diğer best practice’lere çok takılmayacağım.
Bu yazıda EF : Microsoft Entity Framework kısaltmasıdır.
Projemiz basit bir blog sistemi. Post ve Category modellerimiz bulunuyor.
Yazımız başlangıç düzeyi bir yazıdır ve temellere odaklanmakta best practice örneği değil. Bilginize.. Sadece veritabanı konfigüraasyonuna odaklanıyoruz.
Projemiz MyBlogApi namespace’i kullanıyor. Yazı boyunca “MyBlogApi” yerine kendi namespace’nizi yapıştırabilirsiniz. Eğer örneğe bağlı kalmak isterseniz projenizi “MyBlogApi” ile oluşturabilirsiniz.
MSSQL – EF Core için Connection String Nasıl Oluşturulur? yazımı da inceleyerek bu string’i nasıl oluşturduğumu öğrenebilirsiniz. Örnekde herhangi bir kullanıcı adı ve şifre doğrulaması olmadan veritabanına bağlanmaktadır. Sizin durumunuzda farklı bir connection string gerekebilir.
Model Tanımlama
Modellerimizi tanımlayalım:
Models/Post.cs
C#
namespace MyBlogApi.Models{publicclassPost{publicint Id {get;set;}publicstring Title {get;set;}=null!;publicstring Content {get;set;}=null!;publicint CategoryId {get;set;}public Category Category {get;set;}=null!;}}
Models/Category.cs
C#
namespace MyBlogApi.Models{publicclassCategory{publicint Id {get;set;}publicstring Name {get;set;}=null!;public ICollection<Post> Posts {get;set;}=new List<Post>();}}
Veritabanı Sınıfı ve Dependency Injection
Veritabanı yapımızı ve modellerimizi EF’e tanıtmak için AppDbContext sınıfımızı oluşturalım.
Sıradaki adımımız Program.cs dosyasına veritabanı giriş bilgilerimizi ve bağlantı sınıfımızı “Dependency Injection” ile tanıtmak.
Bunun için “connection string” değerimizi okuyacağız ve AddDbContect extension method ile sınıfımızı DI’a ekleyeceğiz.
Ekleyeceğimiz satırlar
C#
// Connection stringvar connectionString =builder.Configuration.GetConnectionString("DefaultConnection");// EF Core servisini eklebuilder.Services.AddDbContext<AppDbContext>(options =>options.UseSqlServer(connectionString));
Program.cs son hali:
C#
using Microsoft.EntityFrameworkCore;using MyBlogApi.Data;var builder =WebApplication.CreateBuilder(args);// Connection stringvar connectionString =builder.Configuration.GetConnectionString("DefaultConnection");// EF Core servisini eklebuilder.Services.AddDbContext<AppDbContext>(options =>options.UseSqlServer(connectionString));builder.Services.AddControllers();builder.Services.AddEndpointsApiExplorer();builder.Services.AddSwaggerGen();var app =builder.Build();// Swaggerif(app.Environment.IsDevelopment()){app.UseSwagger();app.UseSwaggerUI();}app.UseHttpsRedirection();app.MapControllers();app.Run();
Son hali paylaşma sebebim, nereye ekleyeceğinizi tam anlamanız için bunu yapıyorum.
Tabloları Oluşturalım
EF sistemi “Code First” bir yaklaşım içerisindedir ve tanımladığımız modellerden otomatik olarak veritabanı tablolarımızı oluşturur. Bunun için aşağıdaki iki komut ile migration oluşturup veritabanımızı güncellememiz gerekir.
Bu yazıda yer alan bilgiler .net 9 için test edilmiştir. Farklı sürümlerde farklı sonuçlar oluşyabilir. Dersimizin amacı başlangıç düzey bir belge oluşturmak.
Bu yazı dizimizde asp.net core web api projemizde jwt kullanarak aşağıdaki işlemleri gerçekleştireceğiz.
Kurulum(Bu yazı): Bu kısımda temel EF Core configurasyonunu ve JWT configurasyonunu yapacağız ve amacımız ilk olarak JWT kısmını çalışır hale getirmek.
Refresh Token sistemi: bu bölümde uygulamamıza refresh token sistemini ekleyeceğiz
Policy & Role bazlı yetkilendirme: Bu bölümde yetkilendirmeye ve rollere odaklanacağız, bu sistemi nasıl daha performanslı hale getirebileceğiz buna kafa yoracağız.
Konumuza JWT Nedir? Sorusu ile başlayalım bununile ilgili daha ayrıntılı bir yazı da yazacağız ama şimdilik kısaca değinelim
JWT’nin açılımı Json Web Token, aslında bildiğimiz bir string’den ibaret ama amacı kullanıcı hakkınca server’a bilgi vermek.
JWT kimliği doğrulanmış kullanıcılar için server tarafından oluşturulan bir string’dir bu string içinde server’in eklediği bilgileri içerir ve kullanııcya iletilir.
Kullanıcı her istek attığında kimliğini doğrulamak için bu tokeni de istekle birlikte sunucuya göndermelidir.
Sunucu tarafında şifrelenmiş olan bu tokenin anahtarı sadece sunucuda vardır ve sunucu açar içini bakar bu token içerisinde kullanıcı ile ilgili hangi bilgiler var.
Ardından eğer valid yani geçerli bir token ise kullanıcının kimliği ve yetlikeri doğrulanır ve bu çerçevede istekler atmasına izin verilir.
Geçersiz ise oturumun geçersiz olduğu kullanıcıya bildirilir.
JWT’nin Faydaları
JWT’nin faydaları saymakla bitmez ama en önemli faydası stateless bir yapıya sahip olmasıdır, ölçeklediğiniz bir sistemde hangi sunucuya istek attığınızın bir önemi kalmaz çünkü jwt her node tarafından doğrulanabilir. Bu JWT nin en önemli faydasıdır, bunun dışında performans konusunda da bizlere yardımcı olur çünkü token valid ise ve cachelenmiş şekilde geçerli olduğunu sorgulayabiliyorsam tekrar veritabanına gitmeme kullanıcının yetkilerini ve kimliğini sorgulamama gerek kalmaz.
Yani hem stateless hem de performanslı bir yapı kurmamızı sağlar.
JWT’nin ne olduğunu ve faydalarını bilmemiz bizim açımızdan önemliydi, bilmediğimiz bir şeyi sistemimize dahil edersek karmaşa dışında bir işe hizmet etmeyecektir.
JWT sistemini anladığımıza göre şimdi yavaştan ASP.NET Core projemizde JWT kurulumuna başlayalım!
JWT KURULUMU
JWT kurulumu için öncelikle bir webapi projesi oluşturuyoruz. Ben konsol üzerinden anlatacağım işlemleri siz isteseniz Visual Studio ile de aynı işlemleri gerçekleştirebilirsiniz.
Proje oluşturma komutu
PowerShell
dotnet new webapi -n JwtAuthDemo
Öncelikle EF Core paketlerini projemize dahil edelim
namespace JwtAuthDemo.Models{publicclassUser{publicint Id {get;set;}publicstring Username {get;set;}=string.Empty;publicstring PasswordHash {get;set;}=string.Empty;publicstring Role {get;set;}="User";// Admin / User gibi roller}}
Sırada DBContext var EF Core ile kullanmak için DBContext sınıfımızı oluşturalım
Dosya:Data/AppDbContext.cs
C#
using Microsoft.EntityFrameworkCore;using JwtAuthDemo.Models;namespace JwtAuthDemo.Data{publicclassAppDbContext: DbContext{publicAppDbContext(DbContextOptions<AppDbContext> options):base(options){}public DbSet<User> Users =>Set<User>();}}
JWT sistemimizin çalışabilir olması için bazı gerekli configurasyonlar var bunları appsettings.json’a ekleyelim.
AuthController sayesinde token oluşturma login/register işlemleri için gerekli endpointlerimizi oluşturmuş olduk.
JWT mizi test etmek için bir test controller oluşturalım
Dosya:Controllers/TestController.cs
C#
using Microsoft.AspNetCore.Authorization;using Microsoft.AspNetCore.Mvc;namespace JwtAuthDemo.Controllers{[ApiController][Route("api/[controller]")]publicclassTestController: ControllerBase{[Authorize][HttpGet("secure")]public IActionResult SecureEndpoint(){returnOk(new{message="You are authorized!"});}[AllowAnonymous][HttpGet("public")]public IActionResult PublicEndpoint(){returnOk(new{message="Anyone can access this"});}}}
Deploy etmeye hazır olsun diye migration larımızı oluşturalımı
PowerShell
dotnet ef migrations add InitialCreatedotnet ef database update
Buraya kadar geldiyseniz artık projemiz hazır ve test edilebilir durumda Postman veya direk tarayıcı üzerinden sistemimizi test edebilirsiniz.
Özet
Bu yazıda sıfırdan bir projeye JWT ile kullanıcı kimliğini doğrulamayı entegre ettik. Herkese açık ve token korumalı endpointler oluşturarak bu özelliğimizi test ettik.
Bir sonraki yazımızda Refresh Token Sistemini bu projemize ekleyeceğiz.
Eğer swagger kullanarak JWT testi yapmak isterseniz:
Cursor gibi yapay zeka araçları ile MVP geliştirirken verim almak için şu yolu izliyorum:
Öncelikle tüm veritabanı tasarımını, migration dosyasını ve modellerini kendim oluşturuyorum.
İkinci adım olarak controller dosyalarını oluşturup örnek API response’larını, status code’ları tanımlıyorum.
Proje içerisinde bırakabildiğim kadar yorum bırakıyorum. Rule dosyalarına gerekli kodlama ve dosya yapısı açıklamalarını ekliyorum. Bunlardan en önemlisi, Cursor’un her özellikten sonra o özellik için .md dosyası oluşturarak kod ve özellik yapısını anlatması.
Veritabanı yapısı ve controller’ları belirlenmiş hiçbir projede Cursor ile sorun yaşamadım.
Bu adımlardan sonra gerekli özellikleri, ekranları, tasarım dosyalarını da tanıtıyorum ve bundan sonrasını tabii ki yapay zekâya bırakıyorum. Yapay zekâ ile MVP oluşturmak çok zevkli ve hızlı, projenin özellikle veritabanı yapısı ile ilgili kararları kendiniz verirseniz MVP noktasında hiçbir sorun yaşamadan hızlı sonuç alabiliyorsunuz.
Tabii yayınlamadan önce, XSS ve form manipülasyonunu önlemek için kodu review’den geçirmek gerekiyor. Bununla ilgili örnek controller’larda eğer önlem aldıysanız yapay zeka genelde aynı yöntemi kullanıyor ve sorunsuz sonuç verebiliyor. Tabii yine de bir incelemek lazım.
Peki ya sonrası?
Bundan sonraki süreçte, back-end noktasında sadece code-completion özelliğini kullanmak lazım. Agent modu çok tehlikeli olabiliyor. Ben özellikle tasarımcı ile çalışacak bütçem olmadığı için back-end’den ziyade front-end’de yapay zekâya başvuruyorum.
Sizlerin Cursor gibi agent’ler ile deneyiminiz nasıl?
Yapay zeka ile kod yazmak günümüzün gerçeği ve bunun dışında kalmayı kimseye öneremem fakat çok fazla güvenlik açığı bıraktığını ve bunların test & kontrol edilmesi gerektiğini bilmemiz gerekiyor.
Bugün ihtiyacımı olan bir link kısaltma sistemini 45dk da cursor ile geliştirdim. Normalde hiç cursor kullanmadan geliştirmek istesem 1-2 günümü alacak bir sistem ayrıntılı istatistik, grafikler barındırıyor.
Sistemi geliştirdikten sonra fark ettim ki çok basit bir uygulama olmasına rağmen yetkilendirme ve güvenlik konusunda yapay zekanın aklı çok karışık. Başlangıçta belirtmeme rağmen kullanıcılar arası yetki geçişi çok kolay ve kendi üye formunu manipüle etmeyi bilen biri admin yetkisini çok kolay bir şekilde elde edebilir.
Özellikle böyle sistemler kolay geliştirildiği için ücretsiz olarak forumlarda vs. paylaşılıyor ve insanlar indirip kullanıyor.
Bu konuda çok dikkatli olmamız ve özellikle güvenlik anlamında kodları incelememiz, kullanıcıların yetki geçişlerini veya sistemin dosya yükleme gibi özelliklerini manipüle etmesine izin vermeyecek şekilde düzenlememiz gerekiyor.
NOT: Biliyorum şimdi çıkıp prompt oluşturuken bunları dökümante etmem gerektiğini söyleyenler olacak… Haklısınız ama prompt içerisinde belirtmiş olsanız bile yapay zeka söylediğinizi yapmayabiliyor, özellikle context arttığında çok öncelikli bir prompt bile girmiş olsanız istediğiniz sonucu vermiyor veya bir özellik eklemek istediğinizde başka bir özellik için güvenlik açığı doğurabiliyor.
Kısacası yapay zeka süreci kısaltsa da projemize yeni bir gereksinim ekliyor, iyi bir kod review gerektiriyor.
Projenin süresini kısaltıp kısaltmadığına siz karar verin…
Şikeyet etmek değil, önlem almak gerekli. Yapay zeka artık yazılım dünyasının bir gerçeği. Kod yazmayı bilmeyenler için değil de zaten kod yazmayı bilip işleri otomatize etmek isteyenler için çok faydalı.
ASP.NET Core projelerimizde middleware tanımlamak önemli bir özellik bu özellik sayesinde http isteklerini filtreleyebilir, tüm isteklerden önce veya sonra kod çalıştırabiliriz.
Middleware Nedir?
Middleware, bir web uygulamasında isteklerin uygulamaya ulaşmadan önce veya sonra ele alınmasını ve gerekli durumlarda gerekli işlemlerin yapılmasını sağlayan sınıflardır. ASP.NET Core Web API veya MVC Rozor Pages projelerinde bir pipeline( işleme hattı ) ile işlenir ve middleware kullanımı çok kolaydır.
Hangi Durumlarda Kullanılır?
Middleware kullanım senaryolarından bazıları şu şekilde:
Loglama: isteklerden önce veya sonra istek ve isteği oluşturan kullanıcı hakkında log kayıtları oluşturabilirsiniz.
Exception Handling: Hataları yakalamak için yapılar kurabilirsiniz. İsteğe bağlı olarak loglama yada belirli hata mesajları gösterme veya yönlendirmeler yapabilirsiniz.
CORS Ayarları: Tarayıcıdan gelen cross orgin yani farklı kaynaklardan gelen istekleri kısıtlayabilirsiniz.
Yetki / Yetkilendirme Kontrolü: Authorizon veya Authentication yapıları kurarak kullanıcıyı ve yetkilerini kontrol edebilirsiniz.
Performans Ölçümü: İsteğin öncesinde ve sonrasında kod çalıştırabildiğimizi söylemiştik, bu sayede uygulamamızın bir isteği ne kadar sürede işlediğini ve sonucu hazırladığını ölçebiliriz.
Rate Limiting, IP veya Lokasyon Kısıtlama: Bir kullanıcının hangi sıklıkla istek atabileceğini limitleyebilir veya uygulamaya erişimini tamamen kısıtlayabilirsiniz.
Liste daha uzar ama genel olarak ne yapılabileceğine değinmiş olduk. Şimdi örnek bir WEB API projesinde middleware ekleyelim.
Basit Middleware Örneği
Kullanıcının en fazla dakikada 100 kez istek atabilmesini sağlayalım.
Middleware classlarına otomatik olarak bir parametre ekleniyor: RequestDelegate next
middleware’i işleme alırken bu parametre üzerinden mevcut isteği tanıyabiliyoruz. Hemen örneğimize geçelim.
Middlewares/RateLimitingMiddleware.cs
C#
// Middlewares/RateLimitingMiddleware.csusing Microsoft.AspNetCore.Http;using System.Collections.Concurrent;using System.Net;publicclassRateLimitingMiddleware{privatereadonly RequestDelegate _next;privatestaticreadonly ConcurrentDictionary<string, List<DateTime>> _requestLog =new();privateconstint LIMIT =100;// max istekprivatestaticreadonly TimeSpan WINDOW =TimeSpan.FromMinutes(1);// sürepublicRateLimitingMiddleware(RequestDelegate next){_next=next;}publicasync Task InvokeAsync(HttpContext context){string ip =context.Connection.RemoteIpAddress?.ToString()??"unknown";var now =DateTime.UtcNow;var requests =_requestLog.GetOrAdd(ip, _ =>new List<DateTime>());lock(requests){// Eski kayıtları temizlerequests.RemoveAll(r =>r<now-WINDOW);if(requests.Count>=LIMIT){context.Response.StatusCode=(int)HttpStatusCode.TooManyRequests;context.Response.Headers["Retry-After"]="60";return;}requests.Add(now);}await_next(context);}}
Bu örnekde dikkat etmemiz gereken bir nokta var o da next isimli bir sonraki request’i temsil eden ve context isimli mevcut request’in objenin sınıfımıza inject edilmiş olması.
Ve sonrasında InvokeAsync isimli fonksiyonumuzun içerisinde isteğimiz ile ilgili gerekli işlemleri yaptıkdan sonra context’i bir sonraki işleme göndermek.
Bu yapı bizim sistemimizin omurgası, bu yapı tüm pipeline da mevcut ve tüm middleware lar birbirine bu şekilde isteği iletiyor.
Diğer kod kısımları örneğimize ait kısımlar.
Şimdi sırada bu oluşturduğumuz middleware’i Program.cs dosyamızda sıraya almak var.
Bunun için basitçe program.cs dosyamızı şu şekilde düzenliyoruz.
C#
// Program.csusing YourNamespace.Middlewares;// doğru namespace olduğuna dikkat etvar builder =WebApplication.CreateBuilder(args);var app =builder.Build();// Rate Limiting Middleware en başta olabilirapp.UseMiddleware<RateLimitingMiddleware>();app.MapControllers();app.Run();
Burada birden fazla middleware eklersek eğer bilmemiz gereken bu middleware’lerin sırasının önemli olduğudur.
Birden fazla middleware olduğunu varsayalım bu durumda akış şu şekilde olacaktır.
Modern web API’lerinde kullanıcıya büyük veri kümeleri sunarken sayfalama (pagination), filtreleme (filtering) ve sıralama (sorting) işlemleri neredeyse kaçınılmaz hale gelmiştir. Özellikle veritabanı sorgularının verimli yönetilmesi, istemci tarafında performansın artırılması ve kullanıcı deneyiminin iyileştirilmesi açısından bu yapıların doğru şekilde uygulanması oldukça kritiktir.
Bu yazıda, .NET Web API projelerinde pagination, filtering ve sorting işlemlerini en iyi uygulama prensipleriyle (best practices) nasıl hayata geçirebileceğimizi adım adım ele alacağız. Başlamadan önce bu kavramların ne anlama geldiğini ve neden bu kadar önemli olduklarını birlikte inceleyelim.
Entity Framework
ASP.NET Core Web API projelerinde kullandığımız veritabanı iletişimini sağlayan pakete verilen isimdir, bir çok ilişkisel veritabanı ile tam uyumludur fakat biz bu yazımızda özellikle MSSQL için kodlama yaparken kullanacağız.
Pagination (Sayfalama)
Pagination, bir web sayfasının daha hızlı açılması ve sunucuya aşırı yük bindirmemesi için bölümlere ayrılması ve kayıtların belirli limlitlerle sayfalara ayrılmış olarak görüntülenmesine verilen isimdir.
Veritabanından veri çekerken bu limitin uygulanması için offset yani verinin başlangıç sırası ve limit yani kaç adet veri çekileceği belirtilerek bu işlem gerçekleştirilir.
Filtering(Filtreleme)
Filtreleme veritabanındaki kayıtları listelerken belirli kriterleri baz alarak eleme işlemine verilen isimdir. Örneğin 1995 ve üzeri model araçlar gibi filtreler veritabanı sorgularına eklenerek bu kriter ile uyumlu olmayan kayıtlar elenir.
Sorting(Sıralama)
Sıralam işlemi web api’lar da sıkça kullanılan bir yöntemdir, bu yöntem ile kayıtlar belli bir kritere göre sıralanır, bazen son eklenen önce bazen ise farklı sütunlara dayalı olarak bir sıralama gerçekleştirilir, örneğin yıl sutunu için azalandan artana şeklinde sorgular yazılır.
Kavramlarımızdan sade bir şekilde bahsettiğimize göre şimdi sırada EF ile .NET Core Web API projesinde CarController içerisindeki GetAll endpoint’i kullanarak bu işlemleri nasıl gerçekleştirdiğimize bakalım.
Bu yazımızın konusu olmadığı için sıfırdan veritabanı bağlantısı ve kontroller oluşturma ve endpoint ile map’leme işlemine odaklanmayacağım, direk controller üzerinden EF kullanarak bu işlemleri CarController üzerinden gerçekleştireceğim.
Siz veritabanı bağlantısını vs zaten kurduysanız bu adımları atlayabilirsiniz.
Veritabanı Bağlantısının Sağlanması
Öncelikle veritabanınıza ait connection string’i projemize dahil editoruz.
Data/AppDbContext.cs
C#
using Microsoft.EntityFrameworkCore;using CarAPI.Models;namespace CarAPI.Data{publicclassAppDbContext: DbContext{publicAppDbContext(DbContextOptions<AppDbContext> options):base(options){}public DbSet<Car> Cars =>Set<Car>();}}
Sonrasında veritabanı context nesnesini Dependency Injection ile kullanabilmek için Program.cs dosyasına builder oluşturulduktan sonraki satıra veritabanı için DI yapımızı ekliyoruz. Buna ek olarak gerekli bazı using direktifleri de ekliyoruz.
Program.cs
C#
using CarAPI.Data;using Microsoft.EntityFrameworkCore;// var builder = ile başlayan satırın hemen soonrasınabuilder.Services.AddDbContext<AppDbContext>(options =>options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
Dependency injection ile veritabanı bağlantı nesnemizi inject ediyoruz.
Belirli konulara odaklanacağımız için Repository – Service pattern’i kullanmayacağım.
Sorgulara pagination, sorting, filtering eklemek için kullandığımız başlıca EF metodları var
.AsQueryable daha sonra build etmek için query başlatıyoruz.
.Where ile herhangi bir sütun için filtreleme ekleyebiliyoruz.
OrderBy ile sorgumuza istediğimiz şekilde sıralama ekleyebiliyoruz
CountAsync ile toplam kayıt sayısını alıyoruz, bu işlem sayfalama için gerekli oluyor.
Temelde yapmak istediğimiz işlemlere bu dördü yetiyor.
Controllers/CarController.cs
C#
using Microsoft.AspNetCore.Mvc;using Microsoft.EntityFrameworkCore;using CarAPI.Data;using CarAPI.Models;using CarAPI.DTOs;namespace CarAPI.Controllers{[ApiController][Route("api/[controller]")]publicclassCarController: ControllerBase{privatereadonly AppDbContext _context;publicCarController(AppDbContext context){_context=context;}/* * Tüm araçları listeleyen endpoint * Bu metod, filtreleme, sıralama ve sayfalama işlemlerini destekler */[HttpGet]publicasync Task<IActionResult>GetAll([FromQuery] CarQueryParameters queryParams){// Veritabanındaki Cars tablosu üzerinden sorguya başlanırvar query =_context.Cars.AsQueryable();// FILTRELEME ADIMI - MinYear değeri varsa, bu yıldan küçük olanlar elenirif(queryParams.MinYear.HasValue)query=query.Where(c =>c.Year>=queryParams.MinYear.Value);// FILTRELEME ADIMI - MaxYear değeri varsa, bu yıldan büyük olanlar elenirif(queryParams.MaxYear.HasValue)query=query.Where(c =>c.Year<=queryParams.MaxYear.Value);// SIRALAMA ADIMI - Kullanıcı hangi kolona göre sıralama istediğini belirtmişse, ona göre sıralanırif(!string.IsNullOrEmpty(queryParams.SortBy)){// IsDescending true ise azalan, değilse artan sıralama yapılırquery=queryParams.IsDescending?query.OrderByDescending(e =>EF.Property<object>(e,queryParams.SortBy)):query.OrderBy(e =>EF.Property<object>(e,queryParams.SortBy));}// TOPLAM KAYIT SAYISI - Sayfalama için kaç toplam kayıt olduğunu hesaplıyoruzvar totalItems =awaitquery.CountAsync();// SAYFALAMA ADIMI - Sayfa numarasına ve sayfa boyutuna göre veriler bölünürvar cars =awaitquery.Skip((queryParams.Page-1)*queryParams.ValidPageSize)// Kaç kayıt atlanacak.Take(queryParams.ValidPageSize)// Kaç kayıt alınacak.ToListAsync();// SAYFALAMA METADATASI - frontend’e bilgi vermek için header üzerinden metadata gönderiyoruzvar metadata =new{queryParams.Page,PageSize=queryParams.ValidPageSize,TotalItems=totalItems,TotalPages=(int)Math.Ceiling(totalItems/(double)queryParams.ValidPageSize)};// Header'a özel bilgi ekleniyor, genelde frontend tarafında pagination bilgisi burada okunurResponse.Headers.Add("X-Pagination",System.Text.Json.JsonSerializer.Serialize(metadata));// İstenilen filtre/sıralama/sayfalama işlemlerine göre sonuçlar döndürülürreturnOk(cars);}}}
Model
Models/Car.cs
C#
namespace CarAPI.Models{publicclassCar{publicint Id {get;set;}publicstring Brand {get;set;}=string.Empty;publicstring Model {get;set;}=string.Empty;publicint Year {get;set;}publicbool IsDeleted {get;set;}=false;}}
Merhaba Dünya, adetini bozmadan bu blog sitemde neler olacak sizlerle paylaşmak istiyorum.
Websitemin amacı bildiklerimi ve öğrendiklerimi hem bana hem de size kaynak olacak şekilde özetleyerek saklamak.
Bir çok chatgpt sohbeti ve internet araştırmasında edindiğim bilgileri parça parça bir yerlere not ediyorum, bu blog sitesi ile birlikte artık bu parça parça metinleri birleştirip bir başvuru kaynağı oluşturmaya çalışacağım.
Websitem ile ilgilendiğiniz ve yazımı okuduğunuz için teşekkür ederim.