Bölüm 10 / 11
API'yi elle kurcalamak
curl, Postman, .http dosyaları, DevTools Network.
11 dakikalık okuma
Önceki dokuz derste API'yi tasarladık, belgeledik ve kodladık. Bu ders bambaşka bir yetenek kazandırıyor: çalışmayan bir API'nin neden çalışmadığını bulmak. Bu, mülakatta doğrudan sorulmayan ama işe alındıktan sonra her gün kullanılan beceridir — ve iyi geliştiriciyi ayıran şey genellikle tam olarak budur. Bir hatayı "sunucuda mı, istemcide mi, arada mı" diye üç dakikada ayırabilmek, saatlerce tahmin yürütmekten farklıdır.
01Ne öğreneceksin
curlile istek atmak ve çıktısını okumak-wile zamanlama ölçmek: DNS, bağlantı, TLS, ilk bayt- Depoda yaşayan
.httpdosyaları ve ortam değişkenleri - Postman/Insomnia: koleksiyon, ortam,
newmanile CI - Koleksiyonlarda sır sızdırma tuzağı
- DevTools Network: zamanlama, önbellek sütunu, "copy as curl"
- CORS hatalarını doğru okumak
- Belirtiden nedene giden teşhis sırası
02curl
Her yerde bulunur, betiklenebilir ve hiçbir şeyi senden gizlemez. Bilinmesi gereken bayraklar:
curl -v https://api.ornek.local/siparisler/42 # -v: istek (>) ve yanıt (<) satırlarını göster
curl -i ... # yanıt başlıklarını gövdeyle birlikte yaz
curl -I ... # yalnız başlık (HEAD isteği)
curl -s ... # ilerleme çubuğunu sustur (betiklerde)
curl -o cikti.json ... # gövdeyi dosyaya yaz
curl -H "Authorization: Bearer $JETON" \
-H "Accept: application/json" ... # başlık ekle
curl -X POST https://api.ornek.local/siparisler \
--json '{"urun":"kitap","adet":2}' # gövde + Content-Type + Accept birlikte
curl --data-binary @istek.json \
-H "Content-Type: application/json" ... # gövdeyi dosyadan gönder
curl -u kullanici:parola ... # temel kimlik doğrulama
curl -L ... # yönlendirmeleri izle (3xx)
curl -k ... # sertifika doğrulamasını ATLA (yalnız yerelde)
curl --http2 ... / --http1.1 ... # protokol sürümünü zorla
-v çıktısında > ile başlayan satırlar senin gönderdiğin,
< ile başlayanlar sunucunun döndüğü satırlardır; * ile
başlayanlar curl'ün kendi notlarıdır (DNS çözümü, TLS el sıkışması, bağlantı yeniden
kullanımı). 1. derste öğrendiğin mesaj yapısını ilk kez canlı gördüğün yer
burasıdır.
Zamanlama ölçmek az bilinen ama teşhiste çok işe yarayan bir yetenektir:
curl -s -o /dev/null -w \
'dns:%{time_namelookup}s baglanti:%{time_connect}s tls:%{time_appconnect}s ilkbayt:%{time_starttransfer}s toplam:%{time_total}s kod:%{http_code}\n' \
https://api.ornek.local/siparisler/42
Bu tek satır, "API yavaş" şikâyetini dört parçaya böler: DNS çözümü mü uzun sürüyor,
TCP bağlantısı mı, TLS el sıkışması mı, yoksa sunucunun ilk baytı üretmesi mi
(time_starttransfer — yani gerçek sunucu işi)? Cevap genellikle sonuncusudur
ama diğer üçünü elemeden bunu söyleyemezsin. 5. hattın 11. dersinde konuştuğumuz "dış
çağrının süresini ölç" kuralının elle karşılığıdır.
Windows'ta bir uyarı: PowerShell'de curl varsayılan olarak
Invoke-WebRequest için bir takma addır ve bayraklar farklı çalışır. Gerçek
aracı çağırmak için curl.exe yazılır.
03.http dosyaları
Elle deneme isteklerinin depoda yaşadığı biçimdir: Visual Studio ve VS Code
(REST Client / yerleşik destek) bu dosyaları doğrudan çalıştırır. Avantajı, koleksiyonun
koddan ayrı bir yerde değil, projenin yanında ve sürüm kontrolünde olmasıdır —
yeni gelen geliştirici git clone yaptığında deneme isteklerini de almış
olur.
@taban = https://localhost:7241/api/v2
@jeton = {{$dotenv JETON}}
### Sipariş getir
GET {{taban}}/siparisler/42
Accept: application/json
Authorization: Bearer {{jeton}}
### Sipariş oluştur
# @name olustur
POST {{taban}}/siparisler
Content-Type: application/json
Idempotency-Key: {{$guid}}
{
"urun": "kitap",
"adet": 2,
"musteriEposta": "a@ornek.local"
}
### Az önce oluşturulanı getir — önceki yanıttan değer alınır
GET {{taban}}/siparisler/{{olustur.response.body.no}}
Authorization: Bearer {{jeton}}
### Koşullu güncelleme (1. ders: If-Match)
PUT {{taban}}/siparisler/42
If-Match: "v7"
Content-Type: application/json
{ "urun": "kitap", "adet": 5 }
Üç pratik: istekler ### ile ayrılır; değişkenler
@ad = değer ile tanımlanır ve ortam dosyalarıyla
(http-client.env.json) geliştirme/test/üretim için ayrı değer kümeleri
tutulur; bir isteğe @name verilirse sonraki istekler onun yanıtından değer
okuyabilir — zincirleme senaryolar böyle kurulur.
Kritik kural: jeton ve parola bu dosyalara yazılmaz. Ortam
dosyası .gitignore'a eklenir ya da değerler ortam değişkeninden okunur.
Depoya kaçmış bir jeton, geçmişten silinmesi zor bir sızıntıdır — 16. hatta git geçmişi
konusunda bu tekrar karşına çıkacak.
7. dersteki OpenAPI belgesinden bu dosyaların üretilebildiğini de hatırla: sözleşme varsa, deneme isteklerini elle yazmak zorunda değilsin.
04Postman ve koleksiyonlar
Postman (ve Insomnia, Bruno) grafik arayüzlü istemcilerdir. Sağladıkları:
| Kavram | Ne işe yarar |
|---|---|
| Koleksiyon | İlişkili isteklerin klasörlenmiş kümesi; ekiple paylaşılır |
| Ortam | {{taban}}, {{jeton}} gibi değişkenlerin ortam başına değerleri |
| Ön/son betikler | İstekten önce jeton almak, yanıttan değer saklamak |
| Testler | Yanıt üzerinde doğrulama (pm.test) — küçük bir sözleşme testi |
| Koleksiyon çalıştırıcı | Tüm isteklerin sırayla çalıştırılması |
| newman | Aynı koleksiyonun komut satırından ve CI'da çalıştırılması |
# CI'da duman testi olarak koleksiyonu çalıştır
newman run siparis-api.postman_collection.json \
-e test-ortami.json --bail
Koleksiyonlar OpenAPI belgesinden içe aktarılabilir; bu, 7. dersteki "belgeden üretilen" listesinin bir maddesiydi ve elle koleksiyon bakımını ortadan kaldırır.
İki tuzak kıdemli seviyede beklenir. Birincisi sır sızdırma: koleksiyona gömülü bir jeton ya da parola, koleksiyon paylaşıldığında ya da buluta senkronlandığında sızar. Değerler her zaman ortam değişkeni olarak tutulur ve paylaşılan koleksiyona konmaz. İkincisi tek gerçek kaynağın kayması: koleksiyon elle bakıldığında bir süre sonra gerçek API'den sapar ve yanlış belge hâline gelir — bu yüzden ya belgeden üretilmeli ya da CI'da çalıştırılıp kırıldığında fark edilmelidir.
Araç seçimi pratik bir karardır: keşif ve ekiple paylaşım için Postman,
tekrarlanabilirlik ve sürüm kontrolü için .http dosyaları, betikleme ve
sunucuda hata ayıklama için curl. Üçü birbirinin alternatifi değil,
tamamlayıcısıdır.
05Tarayıcı DevTools: Network
Sorun bir web arayüzünden geliyorsa asıl kanıt buradadır. Network sekmesinde her satır bir istektir; bilinmesi gerekenler:
- Preserve log açılır — yoksa sayfa yenilendiğinde ya da yönlendirme olduğunda kanıt kaybolur. "Hata çıkıyor ama göremiyorum" şikâyetinin bir numaralı sebebi budur.
- Status sütunu: gri/italik gösterilen kayıtlar önbellekten gelmiştir
(
from disk cache,from memory cache) ya da 304'tür — 1. derste öğrendiğin önbellek mekanizmasının canlı hâli. Bir değişikliğin "neden görünmediği" sorusunun cevabı çoğu zaman bu sütundadır. - Timing sekmesi: Queueing (tarayıcı bağlantı bekliyor — aynı sunucuya çok istek), Stalled, DNS, Initial connection, SSL, Waiting (TTFB) ve Content Download. TTFB uzunsa sorun sunucudadır; Content Download uzunsa yanıt büyüktür (2. dersteki sıkıştırma ve akış konusu); Queueing uzunsa istemci tarafında eşzamanlılık sorunu vardır.
- Copy → Copy as cURL: tarayıcının attığı isteği birebir
curlkomutuna çevirir. Bu, "tarayıcıda çalışıyor, Postman'da çalışmıyor" sınıfı sorunları saniyeler içinde çözer — fark genellikle bir başlıkta ya da çerezdedir. - Filtreleme:
Fetch/XHRile yalnız API çağrıları, arama kutusundastatus-code:500ya damethod:POSTgibi süzgeçler.
CORS hatalarını doğru okumak ayrı bir beceridir çünkü belirti yanıltıcıdır. Tarayıcı konsolunda "blocked by CORS policy" yazar ve geliştirici sunucunun hata verdiğini sanır; oysa sunucu genellikle 200 dönmüştür — engelleyen tarayıcıdır.
Belirti: konsolda "No 'Access-Control-Allow-Origin' header is present"
Gerçek: sunucu yanıt verdi, ama izin başlığını göndermedi → tarayıcı yanıtı JS'e vermedi
Kanıt: Network'te isteği bul; OPTIONS satırı var mı (ön kontrol/preflight)?
Aynı isteği curl ile at → çalışıyorsa sorun kesinlikle CORS yapılandırmasındadır
Ön kontrol (preflight) OPTIONS isteği, özel başlık ya da
PUT/DELETE gibi metotlarda tarayıcı tarafından otomatik
atılır. 5. hattın 7. dersinde "CORS kimlik doğrulamadan sonraya kalırsa ön kontrol isteği
kimliğe takılır ve tarayıcı asıl isteği hiç göndermez" demiştik — o cümlenin kanıtını
Network sekmesinde OPTIONS satırının 401 alışında görürsün.
06Teşhis sırası
"API çalışmıyor" şikâyeti geldiğinde tahmin yürütmek yerine izlenecek sıra:
- İstek gerçekten gitti mi? Network'te satır var mı? Yoksa sorun sunucuda değil, istemci kodundadır (yanlış koşul, hata yutuldu).
- Durum kodu ne? Kod, sorunun sınıfını söyler ve 1 ile 5. derste kurduğun haritayı burada kullanırsın.
- Başlıklar doğru mu?
Content-Type,Accept,Authorization,Origin. 415 ve 406 hataları neredeyse her zaman burada çözülür. - Gövde ne diyor?
ProblemDetailsdöndürüyorsanhataKoduvetraceIdelindedir. - Sunucu logunda ne var?
traceIdile o isteğin tüm satırlarını bul (5. hat, 5. ders). Burada artık tahmin bitmiştir. - Ara katman mı bozuyor? Aynı isteği vekili atlayarak doğrudan uygulamaya at. Sonuç değişiyorsa sorun ters vekilde ya da ağ geçidindedir (5. hat, 13. ders).
| Belirti | Olası neden | İlk bakılacak |
|---|---|---|
| Tarayıcıda çalışıyor, Postman'da çalışmıyor | Çerez ya da eksik başlık | Copy as cURL ile karşılaştır |
| Konsolda CORS hatası | Sunucu izin başlığı göndermiyor | OPTIONS satırı ve Origin |
| 401 alıyorum ama jetonum var | Süre dolmuş, yanlış hedef kitle, başlık adı | Jetonun içeriği ve Authorization |
| 403 alıyorum | Kimlik doğru, yetki yok | Rol/politika (5. hat, 8. ders) |
| 415 alıyorum | Content-Type yanlış ya da eksik | İstek başlıkları |
| Değişikliğim görünmüyor | Önbellek | Status sütunu: 304 / from cache |
| Bazen çalışıyor bazen çalışmıyor | Yük dengeleyici arkasında kopyalar farklı | Sürüm/kopya kimliği başlığı |
| Yerelde çalışıyor, sunucuda 502 | Uygulama ayağa kalkmıyor ya da vekil yanlış | Uygulama logu + vekil logu |
Son olarak üretimde kurcalama kuralları: yazma isteklerini üretimde elle atmak
gerçek veri değiştirir — özellikle Postman'da yanlış ortam seçiliyken. Ortam adlarını
belirgin yap, üretim ortamını ayrı ve dikkat çekici adlandır, yazma isteklerini üretim
koleksiyonuna hiç koyma. Ayrıca -k ile sertifika doğrulamasını atlama
alışkanlığı yerelde kalmalıdır; üretimde sertifika hatası, çözülmesi gereken gerçek bir
sorundur. Trafiği daha derin incelemek gerekirse mitmproxy ya da Fiddler gibi araçlar
vardır; bunlar TLS'i açtıkları için yalnız kendi sistemlerinde ve izinle
kullanılır.
07Mini alıştırma — Teşhis pratiği
Yaklaşık 20 dakika · 5. hatta yazdığın servis + tarayıcı
- Ham mesaj:
curl -vile bir GET at ve çıktıdaki*,>,<satırlarını ayır. - Zamanlama:
-wile DNS, bağlantı, TLS ve ilk bayt sürelerini ölç. Toplam sürenin çoğu nerede geçti? .httpdosyası: projeyeistekler.httpekle; taban adresi değişken yap, oluşturma isteğine@namever ve sonraki istekte dönen numarayı kullan.- Sır disiplini: jetonu dosyaya yazmadan ortam dosyasından oku ve o dosyayı
.gitignore'a ekle. - 415 üret: POST isteğini
Content-Typebaşlığı olmadan gönder. Hangi kodu aldın? Başlığı ekleyip tekrarla. - Önbellek kanıtı:
ETagdönen bir ucu tarayıcıda iki kez çağır ve Network'te ikinci isteğin durumunu (304 / from cache) gör. - CORS: farklı bir kaynaktan (basit bir HTML sayfası) API'yi
fetchile çağır. Konsolda ne yazıyor? Network'teOPTIONSsatırı var mı? Aynı isteği curl ile atınca çalışıyor mu — bu ne anlama geliyor? - Copy as cURL: tarayıcının attığı bir isteği curl'e çevir ve terminalde çalıştır. Hangi başlıklar tarayıcı tarafından ekleniyormuş?
- Uçtan uca teşhis: servisinde bilerek bir hata bırak (yanlış bağlantı
dizesi), isteği at ve yukarıdaki altı adımı sırayla uygulayarak nedeni
traceIdüzerinden logda bul.
08Kendini sına
curl ile hangi bilgileri elde edersin?
-v ile isteğin ve yanıtın ham satırları görülür:
> gönderilen, < alınan, * curl'ün kendi
notları (DNS, TLS el sıkışması, bağlantı yeniden kullanımı). -i yanıt
başlıklarını gövdeyle, -I yalnız başlıkları (HEAD) getirir; -H
başlık ekler, --json gövdeyle birlikte Content-Type ve
Accept'i ayarlar, -L yönlendirmeleri izler, -u
temel kimlik doğrulama yapar, -k sertifika doğrulamasını atlar (yalnız
yerelde kullanılmalı). Teşhiste en değerlisi -w ile zamanlama ölçmektir:
time_namelookup, time_connect, time_appconnect
(TLS) ve time_starttransfer (ilk bayt) değerleri "API yavaş" şikâyetini dört
parçaya böler; ilk bayt uzunsa sorun gerçekten sunucunun işindedir, diğerleri uzunsa ağ,
DNS ya da TLS tarafındadır. PowerShell'de curl bir takma ad olduğu için
gerçek aracı çağırmak üzere curl.exe yazılır.
.http dosyaları ile Postman koleksiyonlarını karşılaştır.
.http dosyaları düz metindir ve projenin yanında,
sürüm kontrolünde yaşar; Visual Studio ve VS Code doğrudan çalıştırır. İstekler
### ile ayrılır, @ad = değer ile değişken tanımlanır, ortam
dosyalarıyla geliştirme/test/üretim için ayrı değer kümeleri tutulur ve bir isteğe
@name verilerek sonraki istekler onun yanıtından değer okuyabilir. Avantajı
tekrarlanabilirlik ve kod incelemesine girmesidir. Postman/Insomnia grafik arayüzlüdür;
koleksiyon, ortam, ön/son betikler, pm.test ile doğrulamalar ve koleksiyon
çalıştırıcı sunar, newman ile CI'da duman testi olarak koşturulabilir ve
OpenAPI belgesinden içe aktarılabilir. İki tuzağı vardır: koleksiyona gömülü jeton
paylaşıldığında ya da buluta senkronlandığında sır sızar — değerler ortam
değişkeninde tutulur; ve elle bakılan koleksiyon zamanla gerçek API'den saparak yanlış
belgeye dönüşür — bu yüzden belgeden üretilmeli ya da CI'da çalıştırılmalıdır. Üçü
birbirinin alternatifi değildir: keşif ve paylaşım için Postman, tekrarlanabilirlik için
.http, betikleme ve sunucuda hata ayıklama için curl.
DevTools Network sekmesinde neye bakarsın?
Önce Preserve log açılır, yoksa yenileme ya da yönlendirmede
kanıt kaybolur. Status sütununda gri/italik kayıtlar önbellekten gelmiştir
(from disk cache) ya da 304'tür — "değişikliğim neden görünmüyor" sorusunun
cevabı genellikle buradadır. Timing sekmesi süreyi parçalara ayırır: Queueing
(aynı sunucuya çok istek), DNS, Initial connection, SSL, Waiting (TTFB) ve Content
Download; TTFB uzunsa sorun sunucuda, Content Download uzunsa yanıt büyük (sıkıştırma ve
akış konusu), Queueing uzunsa istemci tarafında eşzamanlılık sorunu vardır.
Copy as cURL, tarayıcının attığı isteği birebir komuta çevirir ve "tarayıcıda
çalışıyor, Postman'da çalışmıyor" sınıfı sorunları saniyeler içinde çözer — fark
genellikle bir başlıkta ya da çerezdedir. Filtreleme için Fetch/XHR ve
status-code:500 gibi süzgeçler kullanılır.
CORS hatasını nasıl okursun? "API çalışmıyor" şikâyetinde hangi sırayı izlersin?
CORS hatasında belirti yanıltıcıdır: konsolda "blocked by CORS
policy" yazar ve sunucunun hata verdiği sanılır, oysa sunucu genellikle 200
dönmüştür — yanıtı JavaScript'e vermeyen tarayıcıdır, çünkü izin başlığı
(Access-Control-Allow-Origin) yoktur. Kanıt Network sekmesindedir: özel
başlık ya da PUT/DELETE gibi metotlarda tarayıcı otomatik bir
OPTIONS ön kontrol isteği atar; o satırın durumu bakılır. Aynı istek curl ile
çalışıyorsa sorun kesinlikle CORS yapılandırmasındadır. Teşhis sırası: (1) istek gerçekten
gitti mi — Network'te satır yoksa sorun istemci kodundadır; (2) durum kodu ne — sorunun
sınıfını söyler; (3) başlıklar doğru mu — 415 ve 406 neredeyse her zaman burada çözülür;
(4) gövde ne diyor — ProblemDetails varsa hata kodu ve
traceId elindedir; (5) sunucu logunda traceId ile o isteğin
satırlarını bul; (6) ara katman mı bozuyor — aynı isteği vekili atlayarak doğrudan
uygulamaya at, sonuç değişiyorsa sorun ters vekilde ya da ağ geçidindedir. Üretimde
yazma isteklerini elle atmaktan kaçınılır ve ortam adları belirgin tutulur.
09Sınav köşesi
Yazılıda nasıl sorulur
(1) Verilen bir isteği curl komutu olarak yazınız (başlık, gövde,
metot) — en sık çıkan uygulamalı soru. (2) -v, -i,
-I, -L, -H, -d ne yapar.
(3) .http dosyası nedir, avantajı nedir. (4) Postman koleksiyonu ve ortam
kavramları; newman ne işe yarar. (5) DevTools Network'te TTFB nedir.
(6) Bir isteğin önbellekten geldiği nasıl anlaşılır. (7) CORS hatası nedir, sunucu mu
tarayıcı mı engelliyor. (8) Ön kontrol (preflight) isteği ne zaman atılır. (9) "401
alıyorum ama jetonum var" — olası nedenler. (10) Üretimde API kurcalarken hangi
önlemler alınır.
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Yaklaşım — "bir API sorununda tahmin yürütmem; belirtiden nedene giden
sabit bir sıram var: istek gitti mi, kodu ne, başlıkları doğru mu, gövdesi ne diyor,
logda traceId ile ne var, ara katman mı bozuyor." (2) Araçlar —
"curl'ü betikleme ve sunucuda hata ayıklama için, .http
dosyalarını depoda tekrarlanabilir denemeler için, Postman'ı keşif ve ekiple paylaşım
için kullanırım." (3) Ölçüm — "'yavaş' şikâyetinde curl -w ile DNS,
bağlantı, TLS ve ilk bayt sürelerini ayırırım; sorunun sunucuda mı ağda mı olduğunu
böyle söylerim." (4) Tarayıcı — "Network'te Preserve log açar, Status sütunundan
önbellekten geleni ayırır, Copy as cURL ile tarayıcı isteğini terminale taşırım."
(5) Kıdem vuruşu — "CORS hatasında sunucuyu suçlamam: sunucu genelde 200 dönmüştür,
engelleyen tarayıcıdır — kanıt OPTIONS ön kontrol satırıdır ve aynı
istek curl ile çalışıyorsa sorun kesinlikle yapılandırmadadır. Jetonları koleksiyonlara
ya da .http dosyalarına yazmam, ortam değişkeninde tutarım; üretim
ortamına yazma isteği koymam."
