API Create Read Update Delete İşlemleri

CRUD işlemlerini gerçekleştirebilmemiz için küçük bir todo list uygulaması yapacağız iyi okumalar.

API Ayağa Kaldırma

İlk olarak uygulamayı terminal üzerinden oluşturuyoruz. Tabi önce bir versiyon kontrolü yapmakta fayda var uyumsuzluk olmaması için.

dotnet --version
8.0.100
dotnet new web

Bu kodu çalıştırdığımızda bize şu şekilde bir ağaç yapısı vermesi lazım.

├── appsettings.Development.json
├── appsettings.json
├── bin
│   └── Debug
├── obj
│   ├── Debug
│   ├── project.assets.json
│   ├── project.nuget.cache
│   ├── web.csproj.nuget.dgspec.json
│   ├── web.csproj.nuget.g.props
│   └── web.csproj.nuget.g.targets
├── Program.cs
├── Properties
│   └── launchSettings.json
├── req.http
├── r.py
├── web.csproj
└── web.sln

Üzerinde çalışacağımız dosya Program.cs hızlıca içeriğine bakalım.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

Derinlemesine girmeden kısaca buradan bahsedecek olursak uygulamayı oluşturduğumuz yer.

app.MapGet("/", () => "Hello World!");

Bizi asıl ilgilendiren kısım burası aslında. Oluşturduğumuz app objesinin MapGet metot'dunu kullanarak bir API ucu oluşturuyoruz ve program GET isteklerini dinliyor. "/" bu bizim API ucumuz eğer buraya bir GET isteği atılırsa "Hello World!" yazısı isteği atan makinaya dönderilecek.

app.Run()

dotnet run komutu ile uygulamamızı çalıştırdığımızda dotnet Program.cs içerisindeki kodları derleyip çalıştıracak. Run() komutu uygulamamızın sürekli olarak dinleme yapmasını sağlıyor.

Şimdi uygulamayı çalıştıralım ve ne olduğu hakkında biraz daha bilgi sahibi olalım.

user@hostname:~/web$ dotnet run
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5225
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/user/web

Uygulamayı dotnet run kullanarak çalıştırdığımızda server'dan gelen istekleri dinlemeye hazır. Şimdi uygulamanın bize ne söylediğine tek tek bakalım.

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5225

Uygulama hangi url ve port üzerinde dinleme yaptığını söylüyor. localhost'da 5225 port'u dinliyor.

info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.

Uygulamayı nasıl kapatabileceğimizi söylüyor. Ctrl+C'ye basarak kapatabiliriz.

info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development

Uygulamanın Development yani geliştirme ortamında çalıştığını söylüyor.

info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/user/web

Uygulamanın çalıştığı dizinin ne olduğunu'da buradan öğrenebiliriz mesela benimki /home/user/web miş.

Uygulamanı çalıştırdığımıza göre ilk http isteğini atıp ne olduğunu görebiliriz. Tabi bir url'ye istek atmanın bir sürü yolu var ama ben linux kullanıyorum ve python ile istek atmam gerek çünkü racon bu.

import requests as r

def post(url,endpoint,data):
    url = url + "/" + endpoint 
    headers = {"Content-type": "application/json"}
    response = r.post(url,json=data, headers=headers)
    print(response.status_code,response.text)

def get(url,endpoint):
    url = url +  "/" + endpoint 
    response = r.get(url)
    print(f"[+] {response.status_code} \n[+] {response.text}")

def delete(url,endpoint):
    url = url + "/" + endpoint
    response = r.delete(url)
    print(f"[+] {response.status_code} \n[+] {response.text}")

Şöyle kısa bir python kodumuz var sunucuya POST ,GET ve DELETE istekleri atmamızı sağlıyor ve sunucudan gelen yanıtı terminal ekranına yazdırıyor. Bu kodu uygulamayı çalıştırdığımız dizine .py uzantılı bir dosya olarak kaydediyoruz. Sonrasında python shell'imizi açıyoruz ve sunucumuza bir GET isteği yolluyoruz.

user@hostname:~/web$ python3 
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import r 
>>> url = "http://localhost:5225"
>>> r.get(url,"")
[+] 200 
[+] Hello World!

Artık bir çayı hakettik sunucumuz çalışıyor bize 200 status kodu döndü yani istek başarıyla gerçekleşti ve Hello World! yazısını dönderdi. Şimdi CRUD işlemlerini gerçekleştirebilmemiz için gerekli API uçlarını sunucumuzda oluşturabiliriz. Tabi bunları yazmadan önce server'ımızı kısa bir süreliğine Ctrl+C ile kapatabiliriz.

CRUD İşlemleri

using Microsoft.AspNetCore.Http.HttpResults;

İlk olarak ihtiyacımız olan kütüphaneleri içe aktarıyoruz.

public record Todo(int Id, string Name, DateTime DueDate, bool IsCompleted);

Verilerimizi saklamamızı kolaylaştırması için bir record objesi oluşturuyoruz.

NOT: Yukarıdaki satırı uygulamanızın en alt satırına yazmanız gerek.

var todos = new List<Todo>();

API ucumuza gelen verileri saklamak için bir liste oluşturuyoruz. Liste sadece Todo türündeki verileri tutabilir.

Create

İlk API ucumuzu yazma zamanınmız geldi öncelikle CRUD işlemlerinden Create ile başlayalım. Create adından da anlayabileceğiniz üzere verinin oluşturulduğu yerdir. Verinin oluşturulabilmesi için API ucuna veri gönderilmesi gerekir ve bu da POST istekleri ile yapılır. Şimdi POST isteklerini dinleyen bir API ucu yazalım

app.MapPost("/todos", (Todo task) => {
    todos.Add(task);
    return TypedResults.Created("/todos/{id}",task);
});

Burada kafanıza yatmayan bir yer kalmaması için tek tek inceleyelim.

app oluşturduğumuz uygulama MapPost içerisinde bulunan bir method ve POST isteklerini dinlememizi sağlıyor.

"/todos" verilerin gönderileceği yer yani istek atılacak adress http://localhost:5225/todos olmuş oluyor.

(Todo task) gelen verinin bizim oluşturduğumuz objeye uygun olması gerekiyor. Gönderilen veriler bizim task objemizdeki değerlerle eşitleniyor. (Bunu birazdan POST isteği attığımız zaman daha iyi anlayacaksınız).

todos.Add(task); sonrasında bu değerleri daha önceden oluşturduğumuz todos listesine Add() methodu ile ekliyoruz.

return TypedResults.Created("/todos/{id}",task); Kullanıcıya 201 yanıtını ve oluşturduğu objeyi dönderiyoruz. Yani verinin başarıyla oluşturulduğunu söylüyoruz.

Veriyi oluşturduğumuza göre artık onu okuyabiliriz.

Read

Şimdi sakladığımız verileri paylaşma zamanı geldi burada iki api ucu yazacağız birinde verinin tamamını diğerinde ise istenilen veriyi döndereceğiz.

app.MapGet("/todos", () => todos);

Kısa ve öz GET isteklerini MapGet ile dinliyoruz ve eğer "/todos" uzantısına bir GET isteği gelirse todos listemizin tamamını istek atan makinaya döndeririyoruz.

app.MapGet("/todos/{id}", Results<Ok<Todo>, NotFound> (int id) => {
    var targetTodo = todos.SingleOrDefault(t => id == t.Id);
    return targetTodo is null ? TypedResults.NotFound() :TypedResults.Ok(targetTodo);
});

"/todos/{id}" gördüğünüz üzere id kısmı süslü parantez içerisinde ve (int id) olarak fonksiyon içerisine parametre olarak verilmiş yani isteği atan kişi şuna benzer bir istek atacak "/todos/1"

SingleOrDefault(t => id == t.Id); methodu aracılığı ile gönderilen get isteğindeki id ile liste içerisindeki objelerden id'si eşleşen var mı diye. Bu arama sonucunda elimize ne geçerse bunu targetTodo içerisinde saklıyoruz. Yani sonuç null veya bir objeye eşit olabilir.

Ve son kısımda eğer targetTodo null ise TypedResults.NotFound() 404 status code dönderiyoruz değilse TypedResults.Ok(targetTodo) 200 status code ile bulduğumuz objeyi dönderiyoruz.

Delete

Evet son API ucumuzu yazma zamanı geldi verilerin silineceği API ucu dadada dan.

app.MapDelete("/todos/{id}", (int id) => {
    todos.RemoveAll(t => id == t.Id);
    return TypedResults.NoContent();
});

MapDelete methodu DELETE isteklerini dinliyor. Mesela bir makina böyle bir istek attı "/todos/1" RemoveAll(t => id == t.Id); methodu ile bu id'ye sahip bütün objeleri liste içerisinden siliyoruz ve TypedResults.NoContent(); ile 204 status code dönderiyoruz.

Aşağıda kodun tamamını bulabilirsiniz.

using Microsoft.AspNetCore.Http.HttpResults;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// app.MapGet("/", () => "Hello World!");

var todos = new List<Todo>();

app.MapGet("/todos", () => todos);

app.MapGet("/todos/{id}", Results<Ok<Todo>, NotFound> (int id) => {
    var targetTodo = todos.SingleOrDefault(t => id == t.Id);
    return targetTodo is null ? TypedResults.NotFound() :TypedResults.Ok(targetTodo);
});

app.MapPost("/todos", (Todo task) => {
    todos.Add(task);
    return TypedResults.Created("/todos/{id}",task);
});

app.MapDelete("/todos/{id}", (int id) => {
    todos.RemoveAll(t => id == t.Id);
    return TypedResults.NoContent();
});

app.Run();

public record Todo(int Id, string Name, DateTime DueDate, bool IsCompleted);

Test

Şimdi uygulamayı test etme zamanı.

user@hostname:~/web$ dotnet run                                                                                               
Building...                                                                                                                              
info: Microsoft.Hosting.Lifetime[14]                                                                                                     
      Now listening on: http://localhost:5225                                                                                            
info: Microsoft.Hosting.Lifetime[0]                                                                                                      
      Application started. Press Ctrl+C to shut down.                                                                                    
info: Microsoft.Hosting.Lifetime[0]                                                                                                      
      Hosting environment: Development                                                                                                   
info: Microsoft.Hosting.Lifetime[0]                                                                                                      
      Content root path: /home/user/web           

Sunucumuzu çalıştırdık.

user@hostname:~/web$ python3                                                                                                  
Python 3.10.12 (main, Nov 20 2023, 15:14:05) [GCC 11.4.0] on linux                                                                       
Type "help", "copyright", "credits" or "license" for more information.                                                                   
>>> import r                                                                                                                             
>>> url = "http://localhost:5225"   

python shell'imizi açıyoruz r.py dosyamızı import ediyoruz sonrasında url'mizi bir değişken içerisine atıyoruz.

Şimdi veri oluşturalım (Create).

>>> r.post(url,"todos",{"id":1,"name":"Clear kitchen","dueDate":"2024-03-28T00:00:00","isCompleted":False})                              
201 {"id":1,"name":"Clear kitchen","dueDate":"2024-03-28T00:00:00","isCompleted":false}                                                  
>>> r.post(url,"todos",{"id":2,"name":"Hug your mother","dueDate":"2024-03-28T00:00:00","isCompleted":False})                            
201 {"id":1,"name":"Hug your mother","dueDate":"2024-03-28T00:00:00","isCompleted":false}  

İki tane veri ekledik 201 status code başarıyla eklendiği anlamına geliyor.

Şimdi eklediğimiz verileri okuma zamanı (Read).

>>> r.get(url,"todos")                                                                                                                   
[+] 200                                                                                                                                  
[+] [{"id":1,"name":"Clear kitchen","dueDate":"2024-03-28T00:00:00","isCompleted":false},{"id":2,"name":"Hug your mother","dueDate":"2024
-03-28T00:00:00","isCompleted":false}] 

Güzel ikiside bir liste içerisinde geldi şimdi sadece id'si 2 olanı alalım.

>>> r.get(url,"todos/2")                                                                                                                 
[+] 200                                                                                                                                  
[+] {"id":2,"name":"Hug your mother","dueDate":"2024-03-28T00:00:00","isCompleted":false}    

Son olarak eklediğimiz verilerden bir tanesini silelim.

>>> r.delete(url,"todos/1")                                                                                                              
[+] 204                                                                                                                                  
[+]                                                                                                                                      
>>> r.get(url,"todos")                                                                                                                   
[+] 200                                                                                                                                  
[+] [{"id":2,"name":"Hug your mother","dueDate":"2024-03-28T00:00:00","isCompleted":false}]   

Alıştırma

Farkettiyseniz UPDATE 'i yazmadık onu siz yazabilirsiniz :)

Kaynak

Last updated