Blok 4 · Ders 26
HTTP derinlemesine
İstek ve yanıtın bütün parçaları: metotlar, durum kodları, başlıklar, gövde ve REST mantığı; terminalden istek atmak.
Bu dersin sonunda
- Bir HTTP isteğinin ve yanıtının bütün parçalarını (metot, yol, başlıklar, gövde, durum kodu) gerçek bir mesajda gösterip adlandırabilirsin
- Yapılacak işe göre GET, POST, PUT, PATCH ve DELETE arasından doğru metodu seçebilirsin
- Durum kodunun ilk rakamından sorunun kimde olduğunu söyleyip sık kodları (200, 201, 204, 301, 400, 401, 403, 404, 500) tanıyabilirsin
Content-Typebaşlığıyla JSON gönderebilir, gelen yanıtın türünü okumadan önce denetleyebilirsin- REST kurallarına uyan adresler tasarlayıp kötü tasarlanmış bir adresi fark edebilirsin
- PowerShell'den
curl.exeveInvoke-RestMethodile istek atabilir,curltakma adı ve tırnak tuzaklarından kaçınabilirsin
Bu sayfada
- Hazırlık: klasör ve üç deneme adresi
- Terminalden ilk istek: curl.exe
- İstek ve yanıtın parçaları
- Metotlar: ne yapmak istiyorsun?
- Durum kodları: sunucunun kısa cevabı
- Başlıklar, gövde ve Content-Type
- Authorization: kimliğini göstermek
- REST: kaynaklar ve adresler
- PowerShell'in kendi aracı: Invoke-RestMethod
- Çerez ve oturum: sunucu seni nasıl hatırlar?
- Sık hatalar
- Görevler
- Kendini sına
- Kayıt noktası
Bu ders yeni bir bloğun, sunucu tarafının ilk dersi. Şimdiye kadar hep istek atan taraftaydın: tarayıcı ya da fetch. Bu blokta isteği karşılayan tarafı, yani sunucuyu yazacaksın. Sunucu, gelen HTTP mesajını okuyup HTTP mesajıyla cevap veren bir programdır; o yüzden önce bu mesajların dilini iyi bilmen gerekiyor.
Bu derste mesajı açıp her parçasına tek tek bakacaksın: metot, yol, başlıklar, gövde ve durum kodu. Tarayıcı bu parçaları senden saklar, terminal saklamaz. Dersin çoğunda PowerShell'de çalışacak, curl.exe ve Invoke-RestMethod ile gerçek sunuculara istek atacaksın. Sonunda iyi bir API adresinin nasıl tasarlandığını ve bir sitenin seni çerezle nasıl hatırladığını da bileceksin.
Hazırlık: klasör ve üç deneme adresi
Bu dersin dosyalarını ayrı bir klasörde topla. Son iki derste VS Code'da proje klasörleri açıktı; File → Open Folder... ile yeniden C:\kod\ogren'i aç ve terminale yaz:
PS C:\kod\ogren> mkdir ders26 Directory: C:\kod\ogren Mode LastWriteTime Length Name---- ------------- ------ ----d----- 11.10.2026 15:02 ders26 PS C:\kod\ogren> cd ders26mkdir'in yazdığı tablo yalnızca "klasör oluşturuldu" diyor; tarih sende farklı olur, geçebilirsin. İstem artık PS C:\kod\ogren\ders26>; bu dersteki bütün komutları burada çalıştır.
Bu derste PowerShell'in sürümü önemli, çünkü göreceğin tuzakların çoğu Windows'la gelen eski sürüme özgü. Hangi sürümü kullandığını sor:
PS C:\kod\ogren\ders26> $PSVersionTable.PSVersion Major Minor Build Revision----- ----- ----- --------5 1 26100 94445 ve 1: Windows'la gelen Windows PowerShell 5.1; VS Code'un terminali de varsayılan olarak bunu açar. Build ve Revision sende farklı olabilir. PowerShell 7'yi ayrıca kurduysan VS Code onu açabilir, o zaman ilk sayı 7 çıkar. Ders boyunca "PowerShell 5.1" dediğimiz yerler büyük olasılıkla senin terminalin.
İnternet bağlantısı gerekiyor, çünkü istekleri üç gerçek sunucuya atacaksın:
- Data Dragon (
ddragon.leagueoflegends.com): Riot'un oyun verileri. Yalnızca okumana izin verir. - httpbin (
httpbin.org): ayna sunucu; ne gönderirsen sana aynen geri anlatır. Metotları, başlıkları ve gövdeleri denemek için. - GitHub API (
api.github.com): gerçek ve iyi tasarlanmış bir API. Hesap gerekmez; saatte 60 istek hakkın var.
httpbin bir sunucu taklidi yapar: ona "sil" dediğinde hiçbir şey silinmez, yalnızca "şöyle bir istek aldım" diye anlatır. Kendi sunucunu yazana kadar (Ders 27) metotları ve gövdeleri onunla deneyeceksin.
Terminalden ilk istek: curl.exe
Tarayıcıya bir adres yazınca sayfayı görürsün ama mesajın kendisini görmezsin. Hangi başlıklar gitti, sunucu hangi kodla cevap verdi? fetch ile bunları tek tek console.log'la yazdırman gerekir. curl ise adresini verdiğin sunucuya istek atıp gelen yanıtı terminale olduğu gibi yazan küçük bir programdır. Windows 10 ve 11'de hazır gelir, kurman gerekmez. Ders 24'te onu bir kez kalıp olarak kullanmıştın; API belgelerindeki örneklerin çoğu da curl komutuyla yazılır.
İlk isteği at. Komutu sonundaki .exe ile birlikte, curl.exe diye yaz; nedenini hemen aşağıda göreceksin.
PS C:\kod\ogren\ders26> curl.exe https://ddragon.leagueoflegends.com/realms/tr.json{"n":{"item":"16.20.1","rune":"7.23.1","mastery":"7.23.1","summoner":"16.20.1","champion":"16.20.1","profileicon":"16.20.1","map":"16.20.1","language":"16.20.1","sticker":"16.20.1"},"v":"16.20.1","l":"tr_TR","cdn":"https://ddragon.leagueoflegends.com/cdn","dd":"16.20.1","lg":"16.20.1","css":"16.20.1","profileiconmax":28,"store":null}Gelen şey Türkiye sunucusunun ayar dosyası: v güncel oyun sürümü, l dil. Sende sürüm numarası daha büyük olabilir. curl yalnızca gövdeyi, yani yanıtın asıl içeriğini yazdı. Şimdi aynı komuta -i ekle (include, dahil et):
PS C:\kod\ogren\ders26> curl.exe -i https://ddragon.leagueoflegends.com/realms/tr.jsonHTTP/1.1 200 OKContent-Type: application/jsonContent-Length: 335Connection: keep-aliveAccess-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, HEADAccess-Control-Max-Age: 3000Last-Modified: Tue, 06 Oct 2026 21:17:54 GMTx-amz-server-side-encryption: AES256x-amz-version-id: DdzBYPceetXTplWUhEU8p2wp0dWD2aP.Accept-Ranges: bytesServer: AmazonS3Date: Sun, 11 Oct 2026 13:08:30 GMTETag: "6cd5a3c11e12cf57bb86151cb0be5faa"X-Cache: Hit from cloudfrontVia: 1.1 79d85d2de1f5aa38558ef6bab6274390.cloudfront.net (CloudFront)X-Amz-Cf-Pop: FRA56-P9X-Amz-Cf-Id: fnQc1NsdkE-KrArlsjjLbmlSj4mt9Aly7tZQxlkmX3p2Asu23haDsA==Age: 199 {"n":{"item":"16.20.1","rune":"7.23.1","mastery":"7.23.1","summoner":"16.20.1","champion":"16.20.1","profileicon":"16.20.1","map":"16.20.1","language":"16.20.1","sticker":"16.20.1"},"v":"16.20.1","l":"tr_TR","cdn":"https://ddragon.leagueoflegends.com/cdn","dd":"16.20.1","lg":"16.20.1","css":"16.20.1","profileiconmax":28,"store":null}Gövdenin üstüne bir sürü satır geldi. İlki HTTP/1.1 200 OK: sunucu "tamam" diyor. Altındaki Ad: değer satırları yanıtın başlıkları; sonra bir boş satır ve tanıdık gövde. Başlıkların çoğu sunucunun ve aradaki önbellek servisinin notları (x-amz-…, X-Cache, Via); seni ilgilendirenleri birazdan ayıklayacağız. Date gibi satırlar ve uzun kimlik kodları sende farklı çıkar.
curl'ün en sık kullanacağın seçenekleri:
| Seçenek | Ne yapar? |
|---|---|
-i | Yanıtın başlıklarını da yazar (include: dahil et). |
-I | Yalnızca başlıkları ister, gövdeyi istemez (büyük I). |
-v | Giden isteği de gösterir (verbose: ayrıntılı). |
-X POST | Metodu seçer; yazmazsan GET gider. |
-H "Ad: değer" | İsteğe bir başlık ekler (header: başlık). |
-d "..." | Gövde gönderir (data: veri). -X yazmazsan metodu kendiliğinden POST yapar. |
-L | Yönlendirmeyi takip eder (location: yer). |
-b, -c | -b çerez gönderir, -c gelen çerezleri dosyaya yazar. |
Büyük-küçük harfe dikkat: -i ile -I iki ayrı seçenektir.
curl yazınca Supply values for the following parameters ya da Security Warning çıkıyorNe zaman çıkar? Windows'la gelen PowerShell'de (sürüm 5.1) komutu .exe olmadan, yalnızca curl diye yazınca. Bu PowerShell'de curl, gerçek curl programının değil, PowerShell'in kendi Invoke-WebRequest komutunun takma adıdır (alias): bir komuta verilmiş ikinci isim. Invoke-WebRequest senin -i'ni kendi -InFile (gönderilecek dosya) seçeneği sanar, adresi dosya adı olarak oraya koyar ve eksik kalan adresi sorar:
PS C:\kod\ogren\ders26> curl -i https://ddragon.leagueoflegends.com/realms/tr.json cmdlet Invoke-WebRequest at command pipeline position 1Supply values for the following parameters:Uri:Seçeneksiz yazarsan bu kez bir güvenlik sorusu gelir (9 Aralık 2025 ve sonrasındaki Windows güncellemeleriyle eklendi; güncel olmayan bir bilgisayarda soru çıkmaz):
PS C:\kod\ogren\ders26> curl https://ddragon.leagueoflegends.com/realms/tr.json Security Warning: Script Execution RiskInvoke-WebRequest parses the content of the web page. Script code in the web page might be run when the page isparsed. RECOMMENDED ACTION: Use the -UseBasicParsing switch to avoid script code execution. Do you want to continue? [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"):Nasıl düzelir? Uri: sorusunda Ctrl+C'ye bas. Enter'a basarsan Cannot find drive. A drive with the name 'https' does not exist. gibi anlamsız bir hata gelir, çünkü adres dosya adı sanılmıştı. Güvenlik sorusunda Enter yeter: varsayılan cevap "Hayır" (N), komut Operation cancelled due to security concerns diyerek durur. Sonra komutu curl.exe diye yeniden yaz. Bir adın nereye gittiğini PowerShell'e sorabilirsin:
PS C:\kod\ogren\ders26> Get-Command curl, curl.exe CommandType Name Version Source----------- ---- ------- ------Alias curl -> Invoke-WebRequestApplication curl.exe 8.21.0.0 C:\WINDOWS\system32\curl.execurl takma ad, curl.exe gerçek program. curl.exe yazma alışkanlığı hangi PowerShell'de olursan ol doğru programı çalıştırır. Ders 0'da gördüğün gibi PowerShell'in kendi mesajları Türkçe Windows'ta da büyük olasılıkla İngilizce çıkar.
İstek ve yanıtın parçaları
Ders 18'de tarayıcının attığı bir isteğin parçalarına ilk kez bakmıştın. Şimdi aynı parçaları kendi isteğinde göreceksin. İsteği bu kez -v (verbose, ayrıntılı) ile at; curl senin gönderdiğin isteği de yazar:
PS C:\kod\ogren\ders26> curl.exe -v https://ddragon.leagueoflegends.com/realms/tr.json* Host ddragon.leagueoflegends.com:443 was resolved....* using HTTP/1.x> GET /realms/tr.json HTTP/1.1> Host: ddragon.leagueoflegends.com> User-Agent: curl/8.21.0> Accept: */*>* Request completely sent off...< HTTP/1.1 200 OK< Content-Type: application/json< Content-Length: 335< Connection: keep-alive< Access-Control-Allow-Origin: *...< Age: 15<{"n":{"item":"16.20.1","rune":"7.23.1","mastery":"7.23.1","summoner":"16.20.1","champion":"16.20.1","profileicon":"16.20.1","map":"16.20.1","language":"16.20.1","sticker":"16.20.1"},"v":"16.20.1","l":"tr_TR","cdn":"https://ddragon.leagueoflegends.com/cdn","dd":"16.20.1","lg":"16.20.1","css":"16.20.1","profileiconmax":28,"store":null}* Connection #0 to host ddragon.leagueoflegends.com:443 left intactÜç tür satır var. * ile başlayanlar curl'ün kendi notları (adres bulundu, bağlantı kuruldu, şifreleme yapıldı). > ile başlayanlar bilgisayarından giden istek, < ile başlayanlar sunucudan gelen yanıt. Uzun bölümleri ... ile kısalttık; sende bağlantı satırlarında ağ adresleri de görünür. En sondaki * Connection #0… notu gövdeye yapışık, çünkü gövdenin sonunda satır sonu yok.
İsteğin parçaları
Senin bilgisayarından giden > satırları dört parçadan oluşur:
- İstek satırı:
GET /realms/tr.json HTTP/1.1. Önce metot (GET, "getir"), sonra yol (/realms/tr.json), en sonda protokolün sürümü. Alan adı bu satırda yok; oHostbaşlığında gider. Tek bir sunucu aynı anda birçok siteye hizmet verebildiği için hangi siteyi istediğini böyle söylersin. - Başlıklar:
Hosthangi site,User-Agentisteği hangi programın gönderdiği (tarayıcın burada kendi adını yazar),Accepthangi türde cevap kabul ettiğin (*/*, "ne olursa"). - Boş satır: "başlıklar bitti" demek.
- Gövde: Bu istekte yok.
GETbir şey ister, göndermez. Sunucuya veri yollarken, örneğin yeni bir maç kaydında, gövde dolu olur.
Yanıtın parçaları
Sunucudan gelen < satırları da aynı kalıpta:
- Durum satırı:
HTTP/1.1 200 OK. Sürüm, üç haneli durum kodu ve kısa açıklaması. Durum kodlarına aşağıda ayrı bir bölüm ayırdık. - Başlıklar:
Content-Type: application/jsongövdenin türü,Content-Length: 335gövdenin bayt sayısı;Server,Dateve sunucunun başka notları. - Boş satır.
- Gövde: 335 baytlık JSON.
Metotlar: ne yapmak istiyorsun?
Maç günlüğünü bir sunucuya taşıdığını düşün. Aynı maç listesiyle dört iş yapacaksın: listeye bakmak, yeni maç eklemek, bir maçı düzeltmek, bir maçı silmek. Her iş için ayrı bir adres uydurmak yerine (/macEkle, /macSil) isteğin ilk sözcüğünü değiştirirsin. O sözcüğe metot denir. Buradaki metot, Ders 5'teki dizi metotlarıyla (push, pop) aynı şey değil: HTTP'de isteğin türünü söyleyen sözcük.
| Metot | Ne yapar? | Oyundan |
|---|---|---|
| GET | Okur, getirir; hiçbir şeyi değiştirmez. Gövde göndermez. | Tab'a basıp skor tablosuna bakmak |
| POST | Yeni bir kayıt oluşturur. Yeni kaydın bilgileri gövdede gider. | Dükkândan yeni bir eşya almak |
| PUT | Var olan kaydı tümüyle yenisiyle değiştirir. Kaydın tamamı gövdede gider. | Rün sayfasını baştan yazıp kaydetmek |
| PATCH | Var olan kaydın bir kısmını değiştirir. Yalnızca değişen alanlar gövdede gider. | Rün sayfasında yalnızca bir rünü değiştirmek |
| DELETE | Kaydı siler. Genelde gövde göndermez. | Envanterdeki eşyayı satmak |
İki metodu daha duyacaksın. HEAD, GET'in gövdesiz hâlidir: "yalnızca başlıkları ver". curl'deki -I tam olarak bunu gönderir. OPTIONS'ı ise tarayıcı, başka bir kökene giden bazı isteklerden önce "bu isteğe izin var mı?" diye sormak için kendiliğinden gönderir.
Metodu seçmek: -X
curl varsayılan olarak GET gönderir. Başka bir metot için -X kullan. httpbin'in /anything adresi her metodu kabul eder ve aldığı isteği JSON olarak geri anlatır:
PS C:\kod\ogren\ders26> curl.exe -X DELETE https://httpbin.org/anything/sampiyonlar/teemo{ "args": {}, "data": "", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/8.21.0", "X-Amzn-Trace-Id": "Root=1-6acb9026-2abc33dd6fb92a517a57fda7" }, "json": null, "method": "DELETE", "origin": "203.0.113.7", "url": "https://httpbin.org/anything/sampiyonlar/teemo"}Hiçbir şey silinmedi; httpbin bir ayna. Yansımada isteğin gerçekten DELETE olarak gittiğini (method), hangi adrese gittiğini (url) ve hangi başlıklarla gittiğini (headers) görüyorsun. Gövde göndermediğin için data boş, json da null. origin senin internet (IP) adresin; derste gerçeğini 203.0.113.7 ile değiştirdik.
PUT ile PATCH farkı
İkisi de var olan bir kaydı değiştirir. Fark, gövdeye yazmadığın alanlara ne olduğunda:
PUT kaydı gövdeyle değiştirir, PATCH yalnızca gövdedeki alanı değiştirir.API'lerin çoğu düzeltmeler için PATCH kullanır, çünkü kaydın tamamını yeniden göndermek gerekmez. PUT kullanan bir API'ye eksik bir kayıt gönderirsen ya alanları kaybedersin ya da sunucu "eksik alan var" diye isteği reddeder.
405 METHOD NOT ALLOWED: adres bu metodu kabul etmiyorNe zaman çıkar? Bir adrese, onun kabul etmediği bir metotla istek atınca. httpbin'in /post adresi yalnızca POST kabul eder; curl ise -X vermezsen GET gönderir:
PS C:\kod\ogren\ders26> curl.exe -i https://httpbin.org/postHTTP/1.1 405 METHOD NOT ALLOWEDDate: Sun, 11 Oct 2026 13:15:40 GMTContent-Type: text/htmlContent-Length: 178Connection: keep-aliveServer: gunicorn/19.9.0Allow: POST, OPTIONSAccess-Control-Allow-Origin: *Access-Control-Allow-Credentials: true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>405 Method Not Allowed</title><h1>Method Not Allowed</h1><p>The method is not allowed for the requested URL.</p>Durum satırında 405, başlıklarda Allow: POST, OPTIONS (sende sırası OPTIONS, POST olabilir): sunucu hangi metotları kabul ettiğini de söylüyor. Gövde bir HTML sayfası; birazdan onu JSON sanan bir programın nasıl çöktüğünü göreceksin.
Nasıl düzelir? Allow başlığına ya da API'nin belgesine bak ve doğru metodu -X ile seç: curl.exe -X POST https://httpbin.org/post. Her sunucu bu kurala harfi harfine uymaz: Data Dragon'a POST atarsan 405 değil, 403 Forbidden ve bir HTML hata sayfası gelir. Kod farklı, anlam aynı: bu adres o metodu kabul etmiyor.
Durum kodları: sunucunun kısa cevabı
Yanıtın ilk satırındaki üç haneli sayı, programının "işe yaradı mı?" sorusunun cevabıdır. Yanındaki açıklama sunucudan sunucuya değişebilir (Not Found, httpbin'de NOT FOUND); programlar hep sayıya bakar.
Bir kodu okurken önce ilk rakama bak. 4 ile başlıyorsa sorun senin isteğinde: adres, metot, gövde ya da kimlik bilgisi yanlış. Aynı isteği yeniden göndermek bir şey değiştirmez, isteğini düzeltmen gerekir. Tek istisna 429: isteğin doğru ama çok sık; biraz bekleyip yeniden gönderirsin. 5 ile başlıyorsa sorun sunucuda; isteğin doğru olabilir, biraz bekleyip yeniden dene. En sık göreceğin kodlar:
| Kod ve adı | Ne zaman? | Oyundan |
|---|---|---|
| 200 OK | İstek başarılı, gövdede cevap var. | Maç bulundu. |
| 201 Created | Yeni kayıt oluşturuldu (POST'tan sonra); Location başlığı yeni kaydın adresini verir. | Yeni lobi kuruldu. |
| 204 No Content | Başarılı ama gövde yok (çoğu zaman DELETE'ten sonra). | Arkadaş listenden silindi; söylenecek başka şey yok. |
| 301 Moved Permanently | Adres kalıcı olarak taşındı; yenisi Location'da. | Sunucunun adresi değişti, yenisine git. |
| 302 Found | Geçici yönlendirme. | Bakım sırasında seni duyuru sayfasına almak. |
| 400 Bad Request | İstek bozuk: eksik alan, geçersiz JSON. | Formda doğum tarihi kutusuna harf yazdın. |
| 401 Unauthorized | Kim olduğunu söylemedin ya da kimlik bilgin yanlış. | Giriş yapmadan profiline bakmaya çalıştın. |
| 403 Forbidden | Kim olduğun belli ama bu işe iznin yok. | 30. seviye olmadan dereceli sıraya girmeye çalıştın. |
| 404 Not Found | Böyle bir adres ya da kayıt yok. | Aradığın sihirdar adı yok. |
| 405 Method Not Allowed | Adres var ama bu metodu kabul etmiyor. | İzleyici modunda yetenek kullanmaya çalıştın. |
| 409 Conflict | Var olan bir kayıtla çakışıyor. | Seçtiğin sihirdar adı alınmış. |
| 429 Too Many Requests | Çok sık istek attın, biraz bekle. | Sohbette çok hızlı yazdığın için susturuldun. |
| 500 Internal Server Error | Sunucunun kodunda bir şey patladı. | İstemcide "Beklenmeyen bir hata oluştu". |
| 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout | Sunucu ya da arkasındaki servis cevap veremiyor. | Sunucular bakımda. |
Yönlendirme (redirect): 3xx
3 ile başlayan kodlar "aradığın şey başka yerde" der ve isteği yeni bir adrese yönlendirir (redirect); yeni adres Location başlığındadır. -I ile yalnızca başlıkları iste:
PS C:\kod\ogren\ders26> curl.exe -I http://github.comHTTP/1.1 301 Moved PermanentlyContent-Length: 0Location: https://github.com/http ile istedin, sunucu "kalıcı olarak https'e taşındım" dedi. Tarayıcı ve fetch Location'daki adrese kendiliğinden gider, sen fark etmezsin bile. curl gitmez; gitmesini istersen -L eklersin.
Bu grupta seni başka bir adrese göndermeyen bir kod var: 304 Not Modified (değişmedi). Dersin başındaki curl.exe -i çıktısında ETag: "6cd5…" diye bir satır vardı; o, dosyanın parmak izi. Tarayıcı aynı dosyayı ikinci kez isterken elindeki kopyanın parmak izini de gönderir. Dosya değişmemişse sunucu gövdesiz, kısa bir 304 döndürür ve tarayıcı kendi önbelleğindeki kopyayı kullanır. Maç tekrarını indirmeden önce "bende bu sürüm var" demek gibi: değişmediyse yeniden indirmezsin. Bu işi tarayıcı kendisi yapar, senin kodun genelde 304 görmez.
"Yönlendirme" sözcüğünü Ders 27'de başka bir anlamda da duyacaksın: sunucunun gelen isteği adresine göre kendi içindeki doğru koda göndermesi (routing). Buradaki yönlendirme ise istemciyi başka bir adrese gönderir.
Kodlar programda: yanit.status
fetch her yanıtın kodunu yanit.status'a, açıklamasını yanit.statusText'e koyar. VS Code'un solundaki dosya listesinde ders26 klasörüne sağ tıkla, New File ile durum.mjs adında bir dosya oluştur ve dört adresi sırayla dene:
const adresler = [ "https://ddragon.leagueoflegends.com/realms/tr.json", "https://ddragon.leagueoflegends.com/api/Versions.json", "https://httpbin.org/status/404", "https://httpbin.org/status/500"] for (const adres of adresler) { const yanit = await fetch(adres) console.log(yanit.status, yanit.statusText, yanit.ok)}PS C:\kod\ogren\ders26> node durum.mjs200 OK true403 Forbidden false404 NOT FOUND false500 INTERNAL SERVER ERROR falseDört istek, dört yanıt. fetch hiçbirinde hata fırlatmadı; ok yalnızca 200–299 arasında true.
İkinci adreste versions.json yerine Versions.json yazdık. Çoğu sunucuda yolun büyük-küçük harfi fark eder. Data Dragon olmayan dosya için 404 yerine 403 Forbidden döndürüyor (Ders 15'te de görmüştün); her sunucu kitaba harfi harfine uymaz. O yüzden programın 403'ü de 404'ü de "alamadım" diye karşılayabilmeli.
Programında açıklamaya (statusText) değil, sayıya (status) güven. Açıklamanın sunucudan sunucuya değiştiğini çıktıda da görüyorsun (NOT FOUND büyük harfle). Tarayıcıda boş bile gelebilir: HTTP/2 ile konuşan sitelerde durum satırında açıklama yoktur, statusText boş metin olur.
5xx gerçek hayatta sık görülür: sunucu bir an aşırı yüklenir, bakıma girer ya da arkasındaki servis geç cevap verir. Çoğu zaman aynı istek birkaç saniye sonra düzgün cevap alır. O yüzden 5 ile başlayan bir kod görünce isteğini değiştirmeden önce biraz bekleyip bir kez daha dene.
Başlıklar, gövde ve Content-Type
Sunucuya gövde gönderdiğinde sunucunun onu nasıl okuyacağını bilmesi gerekir: JSON mu, form mu, resim mi? Aynı baytlar farklı türlerde bambaşka anlamlara gelir. Bunu Content-Type başlığı söyler. Yanıtta da aynı başlık senin programına "bu gövde JSON" ya da "bu bir HTML sayfası" der.
Content-Type | Gövdede ne var? |
|---|---|
application/json | JSON |
text/html; charset=utf-8 | HTML sayfası, UTF-8 harf kodlamasıyla |
text/plain | Düz metin |
application/xml | XML (Data Dragon'un hata cevabı böyle) |
image/png | PNG resim |
application/x-www-form-urlencoded | HTML formunun varsayılan gönderimi: isim=Ahri&koridor=orta |
Sık göreceğin başlıklar:
| Başlık | Nerede gider, ne söyler? |
|---|---|
| Host | İstekte: hangi site. |
| User-Agent | İstekte: isteği gönderen program. |
| Accept | İstekte: hangi türde cevap istediğin. |
| Content-Type | İkisinde de: gövdenin türü. |
| Content-Length | İkisinde de: gövdenin bayt sayısı. |
| Authorization | İstekte: kimlik bilgisi (anahtar). |
| Cookie | İstekte: tarayıcının sakladığı çerezler. |
| Set-Cookie | Yanıtta: "bu çerezi sakla". |
| Location | Yanıtta: yönlendirmede yeni adres, oluşturmada yeni kaydın adresi. |
| Allow | Yanıtta: adresin kabul ettiği metotlar. |
curl.exe ile JSON göndermek
Sunucuya JSON göndermek üç şey ister: bir metot (POST), Content-Type: application/json başlığı ve geçerli bir JSON gövdesi. PowerShell'de gövdeyi bir dosyaya yazmak en sorunsuz yoldur; nedenini aşağıdaki hata kartında göreceksin. ders26 klasöründe ahri.json adında bir dosya oluştur:
{ "isim": "Ahri", "koridor": "orta"}Sonra gönder. -H bir başlık ekler, -d gövde gönderir. @ işareti "bu bir dosya adı, içeriğini gönder" demektir ve tırnak içinde yazılmalıdır:
PS C:\kod\ogren\ders26> curl.exe -X POST https://httpbin.org/anything/sampiyonlar -H "Content-Type: application/json" -d "@ahri.json"{ "args": {}, "data": "{ \"isim\": \"Ahri\", \"koridor\": \"orta\"}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "42", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/8.21.0", "X-Amzn-Trace-Id": "Root=1-6acb9027-4a0ff6e71108bcd5136aeeed" }, "json": { "isim": "Ahri", "koridor": "orta" }, "method": "POST", "origin": "203.0.113.7", "url": "https://httpbin.org/anything/sampiyonlar"}data, sunucuya giden ham metin. curl -d ile gönderirken dosyadaki satır sonlarını siler; JSON yine geçerli. json, httpbin'in o metni JSON olarak okuyabildiği hâli: doluysa gövde doğru gitmiş demektir. Yansımadaki headers içinde senin eklediğin Content-Type da var.
Content-Type unutulunca "json": null: gövde form'a düşmüşNe zaman çıkar? curl'e -d ile gövde verip -H "Content-Type: application/json" yazmayı unutunca. curl o zaman gövdeyi bir HTML formu gibi işaretler:
PS C:\kod\ogren\ders26> curl.exe -X POST https://httpbin.org/anything/sampiyonlar -d "@ahri.json"{ "args": {}, "data": "", "files": {}, "form": { "{ \"isim\": \"Ahri\", \"koridor\": \"orta\"}": "" }, "headers": { "Accept": "*/*", "Content-Length": "42", "Content-Type": "application/x-www-form-urlencoded", "Host": "httpbin.org", "User-Agent": "curl/8.21.0", "X-Amzn-Trace-Id": "Root=1-6acb9028-4c9229ce0e8f347244bfe51e" }, "json": null, "method": "POST", "origin": "203.0.113.7", "url": "https://httpbin.org/anything/sampiyonlar"}Sunucu gövdeyi isim=Ahri gibi bir form sandı: bütün JSON'u tek bir alanın adı yaptı, değerini boş bıraktı. json alanı null.
Nasıl düzelir? Gövde gönderen her isteğe gövdenin türünü söyleyen başlığı ekle: -H "Content-Type: application/json". Gerçek bir API bu durumda 400 Bad Request ya da 415 Unsupported Media Type (desteklenmeyen tür) döndürebilir. Kimi sunucu da gövdeyi hiç göremez ve kendi içinde çöküp 500 verir; Ders 28'de kendi sunucunda bunu yaşayacaksın.
Ne zaman çıkar? Gövdeyi dosyaya koymak yerine komutun içine yazınca. Windows'la gelen PowerShell 5.1, dışarıdaki bir programa (curl.exe gibi) metin verirken metnin içindeki çift tırnakları yutar:
PS C:\kod\ogren\ders26> curl.exe -X POST https://httpbin.org/anything/sampiyonlar -H "Content-Type: application/json" -d '{"isim":"Ahri","koridor":"orta"}'{ "args": {}, "data": "{isim:Ahri,koridor:orta}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "24", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/8.21.0", "X-Amzn-Trace-Id": "Root=1-6acb9029-620fbb3d5aef23f91ef8fa89" }, "json": null, "method": "POST", "origin": "203.0.113.7", "url": "https://httpbin.org/anything/sampiyonlar"}Sunucuya {isim:Ahri,koridor:orta} gitti. Anahtarlar tırnaksız kaldığı için bu geçerli bir JSON değil, json alanı null. Terminalde hiçbir hata yok; sorunu ancak sunucunun gördüğüne bakınca fark edersin.
Nasıl düzelir? İki sağlam yol var:
- Gövdeyi bir dosyaya yaz ve
-d "@ahri.json"ile gönder. Her PowerShell sürümünde aynı çalışır. - curl yerine PowerShell'in kendi komutu
Invoke-RestMethod'u kullan (aşağıda).
PowerShell 7.3 ve sonrası tırnakları olduğu gibi geçirir; yukarıdaki komut orada sorunsuz çalışır. İnternette tırnakların önüne ters eğik çizgi konmuş örnekler de görürsün: '{\"isim\":\"Ahri\"}'. Bu yazım 5.1'de işe yarar, ama 7.3 ve sonrasında ters eğik çizgiler de sunucuya gider ve JSON yine bozulur. Örneklerin kimisinin çalışıp kimisinin çalışmamasının sebebi bu fark. Dosya yolu ikisinde de çalışır.
The splatting operator '@' cannot be used to reference variables in an expressionNe zaman çıkar? Dosyadan gövde gönderirken @ahri.json'ı tırnaksız yazınca. İnternetteki curl örnekleri genelde böyle yazılır, ama @ bir sözcüğün başındaysa PowerShell onu kendi özel işareti sanır:
PS C:\kod\ogren\ders26> curl.exe -X POST https://httpbin.org/anything -H "Content-Type: application/json" -d @ahri.jsonAt line:1 char:86+ ... ttpbin.org/anything -H "Content-Type: application/json" -d @ahri.json+ ~~~~~The splatting operator '@' cannot be used to reference variables in an expression. '@ahri' can be used only as an argument to a command. To reference variables in an expression use '$ahri'. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : SplattingNotPermittedParserError: PowerShell komutu daha çalıştırmadan, okurken durdu; istek hiç gitmedi. Mesajın sözünü ettiği "splatting", PowerShell'in kendi bir özelliği; öğrenmen gerekmiyor.
Nasıl düzelir? @ ile başlayan değeri çift tırnak içine al: -d "@ahri.json".
fetch ile JSON göndermek
Programdan JSON göndermek için fetch'e ikinci bir değer verirsin: isteğin ayarlarını taşıyan bir obje. gonder.mjs adında bir dosya oluştur:
const yeniMac = { sampiyon: "Ahri", kill: 7, olum: 2, kazandi: true } const yanit = await fetch("https://httpbin.org/anything/maclar", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(yeniMac)})console.log(yanit.status, yanit.statusText) const yansima = await yanit.json()console.log(yansima.method)console.log(yansima.headers["Content-Type"])console.log(yansima.json)200 OKPOSTapplication/json{ kazandi: true, kill: 7, olum: 2, sampiyon: 'Ahri' }Ayar objesinin üç alanı, curl'deki üç seçeneğin karşılığı:
method: "POST"→-X POST. YazmazsanGETgider.headers: { "Content-Type": "application/json" }→-H. Başlık adını tırnak içinde yazarsın, çünkü içinde tire var.body: JSON.stringify(yeniMac)→-d. Gövde ağdan metin olarak gider; objeyiJSON.stringifyile JSON metnine çevirmen şart.
httpbin json alanındaki anahtarları alfabetik sıraya dizdi; JSON'da anahtarların sırası önemli değildir. Durum kodu 200, çünkü httpbin bir ayna. Gerçek bir API yeni bir kayıt açınca 201 Created döndürür.
TypeError: Request with GET/HEAD method cannot have body.Ne zaman çıkar? fetch'e gövde verip method yazmayı unutunca. fetch'in varsayılan metodu GET'tir ve bir GET isteğinin gövdesi olamaz.
const yanit = await fetch("https://httpbin.org/anything/maclar", { headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sampiyon: "Ahri" })})node:internal/modules/run_main:107 triggerUncaughtException( ^ [TypeError: Request with GET/HEAD method cannot have body.] Node.js v24.20.0Hata senin dosyanı göstermiyor bile; Node'un iç dosyalarından birini gösteriyor. Böyle durumlarda köşeli parantezdeki cümleye güven: "GET ya da HEAD isteğinin gövdesi olamaz."
Nasıl düzelir? Ayar objesine method: "POST" (ya da PUT, PATCH) ekle.
JSON beklerken HTML gelirse
Ders 15'te yanit.ok'a bakmadan json() çağırınca <?xml ile başlayan bir hata almıştın. Aynı hatanın en sık görülen hâli HTML'dir: yanlış metotla, yanlış adresle ya da çökmüş bir sunucuyla konuşunca sunucular çoğu zaman bir HTML hata sayfası döndürür.
SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSONNe zaman çıkar? Yanıtın türüne bakmadan json() çağırınca, gövde de bir HTML sayfası olunca. Denemek için ders26 klasöründe tur.mjs adında bir dosya oluştur. Burada POST bekleyen adrese GET gidiyor; sunucu 405 ve bir HTML sayfasıyla cevap veriyor:
const yanit = await fetch("https://httpbin.org/post")const veri = await yanit.json()console.log(veri.json)<anonymous_script>:1<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">^ SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON at JSON.parse (<anonymous>) ... Node.js v24.20.0< HTML'in ilk harfi; JSON hiçbir zaman < ile başlamaz. Mesajda <!DOCTYPE görürsen sunucudan bir web sayfası gelmiş demektir.
Nasıl düzelir? Gövdeyi okumadan önce iki şeye bak: yanit.ok ve Content-Type. Tür JSON değilse gövdeyi yanit.text() ile düz metin olarak oku; hata sayfasının ne dediğini görürsün:
const yanit = await fetch("https://httpbin.org/post")const tur = yanit.headers.get("content-type") ?? ""console.log(yanit.status, tur) if (yanit.ok && tur.includes("application/json")) { const veri = await yanit.json() console.log(veri)} else { const metin = await yanit.text() console.log("JSON gelmedi. Gövdenin başı: " + metin.slice(0, 14))}405 text/htmlJSON gelmedi. Gövdenin başı: <!DOCTYPE HTMLyanit.headers.get("content-type") yanıttaki bir başlığın değerini verir; başlık yoksa null döner, ?? "" o durumda boş metin koyar (Ders 12). Başlık adının büyük-küçük harfi fark etmez.
Authorization: kimliğini göstermek
Şimdiye kadarki adreslerin hepsi herkese açıktı. Senin maç geçmişin ya da GitHub'daki gizli depoların ise yalnızca sana açık olmalı. Sunucu "sen kimsin?" diye sorar; cevabı her istekte Authorization başlığında verirsin. GitHub'da "beni getir" adresi olan /user'a kimliksiz istek at:
PS C:\kod\ogren\ders26> curl.exe -i https://api.github.com/userHTTP/1.1 401 UnauthorizedContent-Type: application/json; charset=utf-8... { "message": "Requires authentication", "documentation_url": "https://docs.github.com/rest", "status": "401"}401: kim olduğunu söylemedin. Şimdi uydurma bir anahtarla dene. Bearer "taşıyıcı" demek: "bu anahtarı taşıyana izin ver."
PS C:\kod\ogren\ders26> curl.exe https://api.github.com/user -H "Authorization: Bearer yanlis-anahtar-123"{ "message": "Bad credentials", "documentation_url": "https://docs.github.com/rest", "status": "401"}Yine 401, ama mesaj farklı: "kimlik bilgilerin hatalı". Gerçek bir anahtarı GitHub hesabının ayarlarından alabilirdin; bu derste gerek yok. Başlığın sunucuya nasıl vardığını httpbin'in /bearer adresiyle gör; bu adres her anahtarı kabul eder:
PS C:\kod\ogren\ders26> curl.exe https://httpbin.org/bearer -H "Authorization: Bearer deneme-anahtari-123"{ "authenticated": true, "token": "deneme-anahtari-123"}REST: kaynaklar ve adresler
Kendi sunucunu yazınca vereceğin ilk kararlardan biri API'nin adresleri, yani uç noktaları olacak. Herkes aklına geleni yazsa bir API'de /getSampiyonlar, /sampiyonEkle, /sil_sampiyon?id=3 gibi adresler birikir ve API'yi kullanan herkes hepsini tek tek ezberlemek zorunda kalır. REST, adresleri ve metotları ortak bir kurala bağlayan bir tasarım biçimidir: adres neyle ilgili olduğunu, metot ne yapılacağını söyler.
Bu API'ye atılabilecek istekler, anlamları ve başarılı olunca dönen kodlar:
GET /sampiyonlar: bütün şampiyonları listele. Başarıda200.GET /sampiyonlar?rol=buyucu: listeyi süz. Başarıda200.GET /sampiyonlar/ahri: Ahri'yi getir. Başarıda200, böyle bir kayıt yoksa404.POST /sampiyonlar: yeni şampiyon ekle; bilgileri gövdede. Başarıda201veLocationbaşlığı.PUT /sampiyonlar/ahri: Ahri'nin kaydını tümüyle değiştir. Başarıda200ya da204.PATCH /sampiyonlar/ahri: Ahri'nin bir alanını değiştir. Başarıda200ya da204.DELETE /sampiyonlar/ahri: Ahri'yi sil. Başarıda204.GET /sampiyonlar/ahri/kostumler: Ahri'nin kostümlerini listele. Başarıda200.
Tasarım kuralları:
- Adreste ad olsun, fiil olmasın. Fiil zaten metotta:
GET /sampiyonlardoğru,/getSampiyonlaryanlış. - Koleksiyonlar çoğul:
/sampiyonlar,/maclar. Tek kayıt, koleksiyon artı kimlik:/maclar/42. - Küçük harf, sözcükler arasında tire:
/esya-setleri. Türkçe harf kullanma: adresteügibi harfler%C3%BCbiçiminde kodlanır ve okunmaz olur. - Süzme, sıralama ve sayfalama sorgu dizesinde:
/sampiyonlar?rol=buyucu&sirala=isim. - Uzantı yazma:
/sampiyonlar.jsondeğil,/sampiyonlar. TürüContent-Typesöyler. GEThiçbir şeyi değiştirmemeli. Bu kuralın sebebi aşağıda.
GET /getSampiyonlarPOST /sampiyonEkleGET /sampiyonSil?ad=teemoPOST /sampiyonlar/ahri/guncelleGET /Sampiyon_Listesi.jsonGET /sampiyonlarPOST /sampiyonlarDELETE /sampiyonlar/teemoPATCH /sampiyonlar/ahriGET /sampiyonlarÜçüncü satır en tehlikelisi: GET ile silme. Bazı programlar adreslere kendiliğinden GET isteği atar: arama motorlarının botları, sohbet uygulamalarının bağlantı önizlemesi. "Bu bağlantı neymiş" diye baktıkları anda kayıt silinir.
Gerçek bir REST API görmek için GitHub'a bak; adresleri bu kurallarla kurulmuş:
PS C:\kod\ogren\ders26> curl.exe https://api.github.com/users/octocat{ "login": "octocat", "id": 583231, ... "name": "The Octocat", "company": "@github", ... "public_repos": 8, ... "created_at": "2011-01-25T18:44:36Z", "updated_at": "2026-09-22T11:46:44Z"}/users bir koleksiyon (GitHub'daki bütün kullanıcılar), /users/octocat içindeki tek kayıt; octocat GitHub'ın deneme hesabı. /users/octocat/repos o kullanıcının depolarını verir. Kendi kullanıcı adını yazarak kendi profilini de görebilirsin. Olmayan bir kullanıcıyı istersen:
PS C:\kod\ogren\ders26> curl.exe -i https://api.github.com/users/bu-oyuncu-yok-26HTTP/1.1 404 Not FoundContent-Type: application/json; charset=utf-8...X-RateLimit-Limit: 60X-RateLimit-Remaining: 58... { "message": "Not Found", "documentation_url": "https://docs.github.com/rest", "status": "404"}İyi bir API hata durumunda da düzgün bir JSON döndürür: doğru durum kodu ve okunaklı bir mesaj. X-RateLimit-Remaining başlığı bu saat için kaç istek hakkın kaldığını söylüyor; sıfıra inerse 403 ya da 429 alırsın.
Her API REST değildir. Data Dragon bir dosya deposu: adresler .json ile biter ve yalnızca okumaya (GET, HEAD) izin verir; POST atınca 403 döndüğünü yukarıda gördün. Veri okumak için yeterli; kendi API'ni tasarlarken örnek alacağın şey GitHub'ın yapısı.
PowerShell'in kendi aracı: Invoke-RestMethod
curl.exe yanıtı olduğu gibi yazar; içinden tek bir değer almak istersen gözünle aramak zorunda kalırsın. PowerShell'in kendi komutu Invoke-RestMethod gelen JSON'u okuyup bir objeye çevirir. Alanlarına JavaScript'teki gibi noktayla ulaşırsın:
PS C:\kod\ogren\ders26> Invoke-RestMethod https://ddragon.leagueoflegends.com/realms/tr.json n : @{item=16.20.1; rune=7.23.1; mastery=7.23.1; summoner=16.20.1; champion=16.20.1; profileicon=16.20.1; map=16.20.1; language=16.20.1; sticker=16.20.1}v : 16.20.1l : tr_TRcdn : https://ddragon.leagueoflegends.com/cdndd : 16.20.1lg : 16.20.1css : 16.20.1profileiconmax : 28store : PS C:\kod\ogren\ders26> (Invoke-RestMethod https://ddragon.leagueoflegends.com/realms/tr.json).v16.20.1Komutu parantez içine alıp sonuna .v yazınca yalnızca o alan gelir. Değeri null olan store boş görünür. Gövde göndermek için metodu, türü ve gövdeyi ayrı ayrı verirsin. Gövde PowerShell'in kendi metni olarak kaldığı için tek tırnaklı JSON bozulmadan gider:
PS C:\kod\ogren\ders26> (Invoke-RestMethod -Method Post -Uri https://httpbin.org/anything/sampiyonlar -ContentType "application/json; charset=utf-8" -Body '{"isim":"Ahri","koridor":"orta"}').json isim koridor---- -------Ahri ortaYansımanın yalnızca json alanını istedik: sunucu JSON'u doğru almış. -ContentType'ın sonundaki ; charset=utf-8 önemli; sebebini aşağıdaki Türkçe harf kartında göreceksin.
Durum kodu ve başlıklar: Invoke-WebRequest
Invoke-RestMethod yalnızca gövdeyi verir. Durum kodu ve başlıklar için kardeşi Invoke-WebRequest'i kullan. Windows PowerShell 5.1'de ona her zaman -UseBasicParsing ekle; eklemezsen yukarıdaki hata kartında gördüğün güvenlik sorusu gelir:
PS C:\kod\ogren\ders26> Invoke-WebRequest https://ddragon.leagueoflegends.com/realms/tr.json -UseBasicParsing StatusCode : 200StatusDescription : OKContent : {"n":{"item":"16.20.1","rune":"7.23.1","mastery":"7.23.1","summoner":"16.20.1","champion":"16.20.1" ,"profileicon":"16.20.1","map":"16.20.1","language":"16.20.1","sticker":"16.20.1"},"v":"16.20.1","l ":...RawContent : HTTP/1.1 200 OK Connection: keep-alive Access-Control-Allow-Origin: * Access-Control-Allow-Methods: GET, HEAD Access-Control-Max-Age: 3000 x-amz-server-side-encryption: AES256 x-amz-version-id: ...Forms :Headers : {[Connection, keep-alive], [Access-Control-Allow-Origin, *], [Access-Control-Allow-Methods, GET, HE AD], [Access-Control-Max-Age, 3000]...}Images : {}InputFields : {}Links : {}ParsedHtml :RawContentLength : 335 PS C:\kod\ogren\ders26> (Invoke-WebRequest https://ddragon.leagueoflegends.com/realms/tr.json -UseBasicParsing).Headers["Content-Type"]application/jsonStatusCode durum kodu, Headers başlıklar, Content gövde. Tek bir başlığı köşeli parantezle, adını yazarak alırsın.
Hata gelince
Önemli bir fark var: curl ve fetch kötü bir durum kodunda da yanıtı sana verir. Invoke-RestMethod ve Invoke-WebRequest ise 4xx ya da 5xx görünce hata fırlatır. Yanıtın gövdesi varsa hata mesajı gövdenin kendisidir:
PS C:\kod\ogren\ders26> Invoke-RestMethod https://api.github.com/users/bu-oyuncu-yok-26Invoke-RestMethod : {"message":"Not Found","documentation_url":"https://docs.github.com/rest","status":"404"}At line:1 char:1+ Invoke-RestMethod https://api.github.com/users/bu-oyuncu-yok-26+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommandThe remote server returned an error: (404) Not Found. / Uzak sunucu hata döndürdü: (404) Bulunamadı.Ne zaman çıkar? Invoke-RestMethod ya da Invoke-WebRequest 4xx ya da 5xx bir yanıt alınca ve yanıtın gövdesi boşsa. İlk satır Windows'un diline göre değişir. Türkçe Windows'ta:
PS C:\kod\ogren\ders26> Invoke-RestMethod https://httpbin.org/status/404Invoke-RestMethod : Uzak sunucu hata döndürdü: (404) Bulunamadı.At line:1 char:1+ Invoke-RestMethod https://httpbin.org/status/404+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException + FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommandİngilizce Windows'ta ilk satır Invoke-RestMethod : The remote server returned an error: (404) Not Found. olur. Bu cümleyi PowerShell değil, altındaki .NET kütüphanesi yazar; o yüzden PowerShell'in kendi satırları (At line:1 char:1) İngilizce kalırken bu satır Türkçe çıkabilir. Parantezdeki sayı her dilde aynıdır: durum kodu. Yanındaki açıklamanın iki dildeki hâli:
| Kod | Türkçe | İngilizce |
|---|---|---|
| 400 | Hatalı İstek | Bad Request |
| 401 | Onaylanmadı | Unauthorized |
| 403 | Yasak | Forbidden |
| 404 | Bulunamadı | Not Found |
| 405 | Metoda İzin Verilmiyor | Method Not Allowed |
| 409 | Çakışma | Conflict |
| 429 | sunucunun kendi yazdığı (httpbin'de TOO MANY REQUESTS) | sunucunun kendi yazdığı |
| 500 | İç Sunucu Hatası | Internal Server Error |
| 503 | Sunucu Kullanılamıyor | Server Unavailable |
429 için .NET'in hazır bir açıklaması yok; sunucunun durum satırına yazdığı açıklama olduğu gibi gelir.
Nasıl düzelir? Önce parantezdeki sayıya bak ve tablodan anlamını bul: 404 adresi, 401 kimlik bilgisini, 405 metodu kontrol ettirir. Hatanın ayrıntısını görmek istersen aynı isteği curl.exe -i ile at; curl hata fırlatmaz, bütün yanıtı gösterir.
Demacia'nın Kudreti, Işık yerine IsikNe zaman çıkar? Windows PowerShell 5.1'in web komutlarıyla Türkçe metin alırken ya da gönderirken. İki yönde de olur.
Gelirken: Data Dragon Content-Type: application/json yazar ama karakter kodlamasını (charset=utf-8) söylemez. PowerShell 5.1 bu durumda eski bir Batı Avrupa harf tablosu (ISO-8859-1) kullanır ve UTF-8 ile yazılmış ı iki ayrı işarete bölünür:
PS C:\kod\ogren\ders26> (Invoke-RestMethod https://ddragon.leagueoflegends.com/cdn/16.20.1/data/tr_TR/champion/Garen.json).data.Garen.titleDemacia'nın KudretiDoğrusu Demacia'nın Kudreti; aynı adrese curl.exe ile bakarsan doğru görürsün. Veri bozuk değil, yalnızca yanlış tabloyla okunmuş.
Giderken: -ContentType'ta charset yazmazsan PowerShell 5.1 gövdeyi de o eski tabloyla gönderir. Tabloda olmayan Türkçe harfler en yakın Latin harfine dönüşür:
PS C:\kod\ogren\ders26> (Invoke-RestMethod -Method Post -Uri https://httpbin.org/anything -ContentType "application/json" -Body '{"isim":"Lux","unvan":"Işık Leydisi"}').json isim unvan---- -----Lux Isik LeydisiSunucuya Işık Leydisi değil, Isik Leydisi gitti; hata mesajı da yok.
Nasıl düzelir? Gönderirken -ContentType "application/json; charset=utf-8" yaz; o zaman Işık Leydisi olduğu gibi varır. Türkçe metin içeren yanıtlara bakarken curl.exe kullan ya da Node'da fetch ile oku: yanit.json() gövdeyi her zaman UTF-8 olarak okur.
Dört aracı yan yana koyalım:
curl.exe(terminal): gövdeyi olduğu gibi yazar;-i,-I,-vile başlıkları da gösterir.4xxya da5xxgelince de yanıtı yazar. En iyi olduğu iş: mesajın tamamını görmek, API belgelerindeki örnekleri denemek.Invoke-RestMethod(PowerShell): yalnızca gövdeyi verir, JSON'u objeye çevirir.4xxya da5xxgelince hata fırlatır. En iyi olduğu iş: yanıttan tek bir değer almak.Invoke-WebRequest(PowerShell):StatusCode,Headersve metin olarakContentverir.4xxya da5xxgelince hata fırlatır. En iyi olduğu iş: durum koduna ve başlıklara bakmak.fetch(JavaScript):yanit.status,yanit.headers.get(), gövde içinjson()ya datext(). Kötü kodda hata fırlatmaz,yanit.okfalse olur. En iyi olduğu iş: programın içinden istek atmak.
Çerez ve oturum: sunucu seni nasıl hatırlar?
HTTP'nin önemli bir özelliği var: her istek kendi başınadır. Sunucu bir önceki isteği hatırlamaz; aynı kişiden ikinci istek geldiğinde de onun için yabancısın. Peki bir siteye giriş yaptıktan sonra her sayfada yeniden şifre sorulmamasını ne sağlıyor? Çerezler.
Tarayıcıda 2. ve 3. adımdaki işler kendiliğinden olur: Set-Cookie gelince tarayıcı çerezi saklar, sonraki isteklere Cookie başlığını kendisi ekler. curl'de ise bunları elle yaparsın; böylece ne olduğunu görürsün. httpbin'in /cookies/set adresi istediğin çerezi verir:
PS C:\kod\ogren\ders26> curl.exe -i "https://httpbin.org/cookies/set?tema=koyu"HTTP/1.1 302 FOUNDDate: Sun, 11 Oct 2026 13:16:16 GMTContent-Type: text/html; charset=utf-8Content-Length: 223Connection: keep-aliveServer: gunicorn/19.9.0Location: /cookiesSet-Cookie: tema=koyu; Path=/Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>Redirecting...</title><h1>Redirecting...</h1><p>You should be redirected automatically to target URL: <a href="/cookies">/cookies</a>. If not click the link.Tek yanıtta iki şey var: 302 ile "/cookies adresine git" diyen bir yönlendirme ve Set-Cookie: tema=koyu. Şimdi çerezleri gösteren adrese istek at:
PS C:\kod\ogren\ders26> curl.exe https://httpbin.org/cookies{ "cookies": {}}Boş. curl çerezi saklamadı, sunucu da seni hatırlamadı. Çerezi -b ile kendin gönder; curl onu Cookie başlığına koyar:
PS C:\kod\ogren\ders26> curl.exe https://httpbin.org/cookies -b "tema=koyu"{ "cookies": { "tema": "koyu" }}Tarayıcının yaptığını curl'e de yaptırabilirsin: -c gelen çerezleri bir dosyaya yazar, -b o dosyadan okur. Bu kez iki çerez isteyelim. Adresteki & yüzünden tırnaklar şart:
PS C:\kod\ogren\ders26> curl.exe -c cerez.txt "https://httpbin.org/cookies/set?tema=koyu&dil=tr"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>Redirecting...</title><h1>Redirecting...</h1><p>You should be redirected automatically to target URL: <a href="/cookies">/cookies</a>. If not click the link.PS C:\kod\ogren\ders26> curl.exe -b cerez.txt https://httpbin.org/cookies{ "cookies": { "dil": "tr", "tema": "koyu" }}The ampersand (&) character is not allowedNe zaman çıkar? İçinde & olan bir adresi tırnaksız yazınca. & PowerShell'de özel bir işarettir; adresin parçası sayılmaz:
PS C:\kod\ogren\ders26> curl.exe -i https://httpbin.org/cookies/set?tema=koyu&dil=trAt line:1 char:54+ curl.exe -i https://httpbin.org/cookies/set?tema=koyu&dil=tr+ ~The ampersand (&) character is not allowed. The & operator is reserved for future use; wrap an ampersand in double quotation marks ("&") to pass it as part of a string. + CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException + FullyQualifiedErrorId : AmpersandNotAllowedYine bir ParserError: komut okunurken durdu, istek hiç gitmedi.
Nasıl düzelir? Adresi çift tırnak içine al: "https://httpbin.org/cookies/set?tema=koyu&dil=tr". Sorgu dizesi olan adresleri hep tırnakla yazma alışkanlığı edin.
Tarayıcıda çerezleri görmek için F12 ile geliştirici araçlarını aç; Application sekmesinde soldaki Cookies başlığı altında her sitenin çerezleri durur.
Oturum çerezi bir anahtar gibidir: ele geçiren, senin oturumunla istek atar. Bu yüzden sunucular oturum çerezlerine HttpOnly, Secure, SameSite gibi koruma işaretleri koyar; bunları ve gerçek bir giriş sistemini Ders 31'de kuracaksın. cerez.txt gibi dosyaları da Git'e koyma; dersin sonunda onu .gitignore'a ekleyeceksin.
Sık hatalar
Yukarıdaki kartlara ek olarak bu derste karşılaşabileceğin iki hata daha var. Bütün derslerin hataları hata rehberinde toplu durur.
curl: (6) Could not resolve hostNe zaman çıkar? Alan adını yanlış yazınca ya da internet bağlantın yokken. curl alan adını IP adresine çeviremedi (Ders 18'deki DNS); istek hiç gitmediği için durum kodu da yok.
PS C:\kod\ogren\ders26> curl.exe -i https://ddragn.leagueoflegends.com/realms/tr.jsoncurl: (6) Could not resolve host: ddragn.leagueoflegends.comNasıl düzelir? Mesajdaki alan adını harf harf oku: ddragn değil, ddragon. Ad doğruysa internet bağlantını kontrol et. fetch'te aynı durum TypeError: fetch failed olarak görünür (Ders 15).
-H : The term '-H' is not recognizedNe zaman çıkar? API belgelerindeki örnekler çoğu zaman Linux ve macOS'un kabuğu bash için yazılır ve uzun komutu satır sonundaki \ ile böler. PowerShell \'i "komut alt satırda sürüyor" diye anlamaz. Böyle bir örneği terminale yapıştırınca VS Code önce birden çok satır yapıştırdığını söyleyen bir uyarı gösterebilir (görünüşü sürüme göre değişir). Onaylarsan her satır ayrı bir komut olarak, sırayla çalışır:
PS C:\kod\ogren\ders26> curl.exe -X POST https://httpbin.org/anything/sampiyonlar \{ "args": {}, "data": "", ... "method": "POST", ...}curl: (3) URL rejected: Bad hostnamePS C:\kod\ogren\ders26> -H "Content-Type: application/json" \-H : The term '-H' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.At line:1 char:3+ -H "Content-Type: application/json" \+ ~~ + CategoryInfo : ObjectNotFound: (-H:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException PS C:\kod\ogren\ders26> -d '{"isim":"Ahri"}'-d : The term '-d' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.At line:1 char:3+ -d '{"isim":"Ahri"}'+ ~~ + CategoryInfo : ObjectNotFound: (-d:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundExceptionİlk satır tek başına çalıştı: gövdesiz bir POST gitti (data boş, yansımayı kısalttık). Sondaki \ de ikinci bir adres sanıldı (URL rejected: Bad hostname). İkinci ve üçüncü satırlar ise adı -H ve -d olan komutlar sanıldı: is not recognized, "tanınmıyor". Bu, Ders 0'daki node hatasıyla aynı mesaj; PowerShell'in kendi mesajı olduğu için Türkçe Windows'ta da büyük olasılıkla İngilizce görürsün (bu ders Türkçe kurulu bir Windows 11'de denendi).
Nasıl düzelir? Belgedeki örneği uyarlarken üç şeyi değiştir: curl yerine curl.exe yaz, satırları birleştirip \'leri sil, JSON gövdesini bir dosyaya koyup -d "@dosya.json" ile gönder.
Görevler
Görevleri C:\kod\ogren\ders26 klasöründe yap. Önce kendin dene; takılırsan ipuçlarını sırayla aç, çözüme en son bak.
Başlıkları oku
Bu kez gövdeyi değil, yalnızca başlıkları iste. Data Dragon'un sürüm listesine (https://ddragon.leagueoflegends.com/api/versions.json) yalnızca başlıkları isteyen bir istek at ve çıktıdan şu dört soruyu cevapla:
- Durum kodu kaç, ne anlama geliyor?
- Gövdenin türü ne?
- Gövde kaç bayt?
- Tarayıcıya hangi metotlara izin verdiğini söylüyor?
Sonra adresteki versions.json'ı Versions.json yap ve aynı soruları yeniden cevapla.
İpucu 1
Yalnızca başlıkları isteyen seçenek büyük I: curl.exe -I <adres>.
İpucu 2
Durum kodu ilk satırda. Tür Content-Type'ta, uzunluk Content-Length'te yazıyor. Tarayıcıya söylenen metotlar, adında Methods geçen bir CORS başlığında.
Çözümü göster
PS C:\kod\ogren\ders26> curl.exe -I https://ddragon.leagueoflegends.com/api/versions.jsonHTTP/1.1 200 OKContent-Type: application/jsonContent-Length: 5219Connection: keep-aliveAccess-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, HEADAccess-Control-Max-Age: 3000Last-Modified: Tue, 06 Oct 2026 21:17:54 GMTx-amz-server-side-encryption: AES256x-amz-version-id: UR812qMn2N6hyOXrawAE8DdWblr01Ls7Accept-Ranges: bytesServer: AmazonS3Date: Sun, 11 Oct 2026 13:12:37 GMTETag: "35a090c11e950879280872355d11a0ee"X-Cache: Hit from cloudfrontVia: 1.1 e98c62aea2ca07ecaab1da797bdc7fb8.cloudfront.net (CloudFront)X-Amz-Cf-Pop: BUD50-P3X-Amz-Cf-Id: U3MFJhIG8-JsNnN9Yj9jemMTuo7b2uakppBZHUSwPoGFI5SAHRBhsg==Age: 183200 OK: istek başarılı.application/json: gövde JSON.5219bayt. Sende daha büyük olabilir; her yamada listeye yeni bir sürüm eklenir.Access-Control-Allow-Methods: GET, HEAD. Bu bir CORS başlığı: tarayıcıya "başka kökenden gelen sayfalar bu metotlarla istek atabilir" der. 405 kartındakiAllowgibi "sunucu yalnızca bunları kabul eder" anlamına gelmek zorunda değil, ama Data Dragon'da ikisi aynı:POSTatınca403geldiğini 405 kartında gördün.
Gövde görünmedi, çünkü -I bir HEAD isteği gönderir: "yalnızca başlıkları ver".
Sınır denemesi: Versions.json ile sonuç değişir.
PS C:\kod\ogren\ders26> curl.exe -I https://ddragon.leagueoflegends.com/api/Versions.jsonHTTP/1.1 403 ForbiddenContent-Type: application/xmlConnection: keep-aliveAccess-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, HEADAccess-Control-Max-Age: 3000Server: AmazonS3Date: Sun, 11 Oct 2026 13:34:29 GMTX-Cache: Error from cloudfrontVia: 1.1 4d1daf728c8f336e79bd83ec18bb8cb0.cloudfront.net (CloudFront)X-Amz-Cf-Pop: MRS52-P1X-Amz-Cf-Id: 6D2KSTgvyZRkoaGEmRBlxsrvGuyGnpH8cMDXmSqOzPp3JD_Lzp5RvA==Tek harf farkıyla dosya bulunamadı: durum 403, tür application/xml, Content-Length yok. Yol büyük-küçük harfe duyarlı ve Data Dragon olmayan dosya için 404 yerine 403 veriyor. Tarayıcıya söylenen metotlar yine GET ve HEAD.
Kendi GitHub profilin
Ders 7'de bir GitHub hesabı açtın. Invoke-RestMethod ile hesabının üç bilgisini ayrı ayrı yazdır: açık depo sayısı (public_repos), hesabın açılış zamanı (created_at) ve profil adresi (html_url). Kendi adınla denemeden önce octocat ile dene; şu çıktıyı almalısın (değerler zamanla değişebilir):
82011-01-25T18:44:36Zhttps://github.com/octocatSonra olmayan bir kullanıcı adı dene. Ne görüyorsun, durum kodu kaç?
İpucu 1
Adres https://api.github.com/users/ ve arkasına kullanıcı adı.
İpucu 2
Komutu parantez içine alıp sonuna alanın adını yaz: (Invoke-RestMethod <adres>).public_repos. Her bilgi için ayrı bir komut.
İpucu 3
Olmayan kullanıcıda Invoke-RestMethod hata fırlatır. Kırmızı mesajın ilk satırındaki JSON'a bak.
Çözümü göster
PS C:\kod\ogren\ders26> (Invoke-RestMethod https://api.github.com/users/octocat).public_repos8PS C:\kod\ogren\ders26> (Invoke-RestMethod https://api.github.com/users/octocat).created_at2011-01-25T18:44:36ZPS C:\kod\ogren\ders26> (Invoke-RestMethod https://api.github.com/users/octocat).html_urlhttps://github.com/octocatPS C:\kod\ogren\ders26> Invoke-RestMethod https://api.github.com/users/bu-oyuncu-yok-26Invoke-RestMethod : {"message":"Not Found","documentation_url":"https://docs.github.com/rest","status":"404"}...Olmayan kullanıcıda gelen kırmızı hatanın devamını Hata gelince bölümünde görmüştün; burada kısalttık. Durum kodu 404; GitHub bunu hata gövdesine koyduğu JSON'un status alanında da yazıyor.
Dikkat: her komut ayrı bir istek attı; saatlik 60 hakkından dördü gitti. GitHub'dan 5 ile başlayan bir kod gelirse birkaç saniye bekleyip komutu yeniden çalıştır.
Durum kodunun grubu
grup.mjs adında bir dosyaya, bir durum kodunu alıp grubunun adını döndüren durumGrubu(kod) fonksiyonunu yaz:
- 100–199:
"bilgi", 200–299:"başarılı", 300–399:"yönlendirme", 400–499:"istemci hatası", 500–599:"sunucu hatası" - Bu aralıkların dışındaki her sayı:
"geçersiz kod"
Sonra [99, 100, 200, 204, 299, 301, 404, 429, 499, 500, 503, 599, 600] dizisindeki her kodu dene. Beklenen çıktı:
99 geçersiz kod100 bilgi200 başarılı204 başarılı299 başarılı301 yönlendirme404 istemci hatası429 istemci hatası499 istemci hatası500 sunucu hatası503 sunucu hatası599 sunucu hatası600 geçersiz kodİpucu 1
Önce aralığın dışını ele: kod < 100 || kod > 599 ise hemen "geçersiz kod" döndür (Ders 6'daki erken çıkış).
İpucu 2
İlk rakam Math.floor(kod / 100): 404 / 100 sonucu 4.04, aşağı yuvarlayınca 4.
İpucu 3
İlk rakama göre bir else if zinciri kur. Başka bir yol: grup adlarını sırayla bir diziye koy ve ilkRakam - 1 sıradaki elemanı döndür.
Çözümü göster
function durumGrubu(kod) { if (kod < 100 || kod > 599) { return "geçersiz kod" } const ilkRakam = Math.floor(kod / 100) if (ilkRakam === 1) { return "bilgi" } else if (ilkRakam === 2) { return "başarılı" } else if (ilkRakam === 3) { return "yönlendirme" } else if (ilkRakam === 4) { return "istemci hatası" } else { return "sunucu hatası" }} const kodlar = [99, 100, 200, 204, 299, 301, 404, 429, 499, 500, 503, 599, 600]for (const kod of kodlar) { console.log(kod, durumGrubu(kod))}99 geçersiz kod100 bilgi200 başarılı204 başarılı299 başarılı301 yönlendirme404 istemci hatası429 istemci hatası499 istemci hatası500 sunucu hatası503 sunucu hatası599 sunucu hatası600 geçersiz kodSınır değerleri: 99 ve 600 aralığın hemen dışı; 100, 299, 499, 599 grupların ilk ya da son kodları. Sınır denetimini sona bıraksaydın 600 için Math.floor 6 verir ve else onu "sunucu hatası" sayardı. 404.5 gibi küsuratlı bir sayı da "istemci hatası" der; gerçek kodlar hep tam sayıdır.
Maç günlüğü API'si: tasarla ve dene
Ders 23'teki maç günlüğünü bir sunucuya taşıdığını düşün. Her maçın bir kimliği (id), şampiyonu, kill ve ölüm sayısı ve sonucu var. Şu altı iş için metot ve adres yaz; başarıda hangi durum kodunun dönmesi gerektiğini de ekle:
- Bütün maçları listele.
- Yalnızca Jinx'le oynanan maçları listele.
- 7 numaralı maçı getir.
- Yeni bir maç ekle.
- 7 numaralı maçın yalnızca ölüm sayısını 4 yap.
- 7 numaralı maçı sil.
Sonra 4. ve 5. maddeyi httpbin'e gönder: adresin başına https://httpbin.org/anything ekle, gövdeleri mac.json ve olum.json dosyalarından ver. Yansımadaki method, url ve json senin tasarımınla aynı olmalı.
İpucu 1
Koleksiyon /maclar, tek maç /maclar/7. Süzme sorgu dizesinde yapılır.
İpucu 2
Yeni kayıt POST ve 201; kaydın bir kısmını düzeltmek PATCH; silmek DELETE ve çoğu zaman 204.
İpucu 3
Beşinci madde için önce olum.json dosyasını oluştur ({ "olum": 4 }), sonra: curl.exe -X PATCH https://httpbin.org/anything/maclar/7 -H "Content-Type: application/json" -d "@olum.json".
Çözümü göster
- Bütün maçlar:
GET /maclar→200 - Jinx maçları:
GET /maclar?sampiyon=Jinx→200 - 7 numaralı maç:
GET /maclar/7→200 - Yeni maç:
POST /maclar→201ve yeni kaydın adresiyleLocation: /maclar/<yeni id> - Ölüm sayısını düzelt:
PATCH /maclar/7→200ya da204 - Maçı sil:
DELETE /maclar/7→204
İki dosya:
{ "sampiyon": "Jinx", "kill": 12, "olum": 3, "kazandi": true}{ "olum": 4 }PS C:\kod\ogren\ders26> curl.exe -X POST https://httpbin.org/anything/maclar -H "Content-Type: application/json" -d "@mac.json"{ "args": {}, "data": "{ \"sampiyon\": \"Jinx\", \"kill\": 12, \"olum\": 3, \"kazandi\": true}", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "73", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/8.21.0", "X-Amzn-Trace-Id": "Root=1-6acb9067-477fe4be4f9c56d02fd5ac2b" }, "json": { "kazandi": true, "kill": 12, "olum": 3, "sampiyon": "Jinx" }, "method": "POST", "origin": "203.0.113.7", "url": "https://httpbin.org/anything/maclar"}PS C:\kod\ogren\ders26> curl.exe -X PATCH https://httpbin.org/anything/maclar/7 -H "Content-Type: application/json" -d "@olum.json"{ "args": {}, "data": "{ \"olum\": 4 }", "files": {}, "form": {}, "headers": { "Accept": "*/*", "Content-Length": "13", "Content-Type": "application/json", "Host": "httpbin.org", "User-Agent": "curl/8.21.0", "X-Amzn-Trace-Id": "Root=1-6acb9068-0e4f1add39e7f0b55bf0095f" }, "json": { "olum": 4 }, "method": "PATCH", "origin": "203.0.113.7", "url": "https://httpbin.org/anything/maclar/7"}Sınır durumları: gerçek bir API'de olmayan bir maç (/maclar/999) için GET, PATCH ve DELETE 404 döndürmeli; httpbin ayna olduğu için her yola 200 der. Aynı POST'u iki kez gönderirsen gerçek bir API iki ayrı maç açar; aynı PATCH'i iki kez göndermek ise sonucu değiştirmez.
Çıkış yapmak: çerezi silmek
Bir sitede Çıkış yap'a bastığında sunucu tarayıcına "oturum çerezini sil" der. Bunu httpbin'le dene; çerezleri dersteki gibi cerez.txt dosyasında tut. Her adımda curl.exe'ye hangi seçenekleri vereceğini sen bul.
https://httpbin.org/cookies/set?oturum=x9k2adresinden gelen çerezicerez.txt'ye yaz. Bu adımda dosyadaki çerezleri gönderme. Dosyaya yazan seçenek dosyayı baştan yazar; dersteki eski çerezler (tema,dil) böylece dosyadan çıkar.cerez.txt'deki çerezlehttps://httpbin.org/cookiesadresine istek at.- Çıkış yap:
https://httpbin.org/cookies/delete?oturumadresinecerez.txt'deki çerezi gönder, gelen değişikliği yinecerez.txt'ye yaz ve yanıtın başlıklarını da göster.Set-Cookiesatırında sunucu çerezi nasıl siliyor? - 2. adımdaki isteği yeniden at. Çerez nereye gitti?
2. adımda şunu görmelisin:
{ "cookies": { "oturum": "x9k2" }}İpucu 1
Gelen çerezleri dosyaya yazmak -c, dosyadakileri göndermek -b, başlıkları göstermek -i. Bir komutta üçünü birden verebilirsin.
İpucu 2
3. adımın komutu: curl.exe -i -b cerez.txt -c cerez.txt "https://httpbin.org/cookies/delete?oturum". Set-Cookie satırındaki Expires (son kullanma tarihi) hangi yılı gösteriyor?
Çözümü göster
PS C:\kod\ogren\ders26> curl.exe -c cerez.txt "https://httpbin.org/cookies/set?oturum=x9k2"<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>Redirecting...</title><h1>Redirecting...</h1><p>You should be redirected automatically to target URL: <a href="/cookies">/cookies</a>. If not click the link.PS C:\kod\ogren\ders26> curl.exe -b cerez.txt https://httpbin.org/cookies{ "cookies": { "oturum": "x9k2" }}PS C:\kod\ogren\ders26> curl.exe -i -b cerez.txt -c cerez.txt "https://httpbin.org/cookies/delete?oturum"HTTP/1.1 302 FOUNDDate: Sun, 11 Oct 2026 22:51:28 GMTContent-Type: text/html; charset=utf-8Content-Length: 223Connection: keep-aliveServer: gunicorn/19.9.0Location: /cookiesSet-Cookie: oturum=; Expires=Thu, 01-Jan-1970 00:00:00 GMT; Max-Age=0; Path=/Access-Control-Allow-Origin: *Access-Control-Allow-Credentials: true <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><title>Redirecting...</title><h1>Redirecting...</h1><p>You should be redirected automatically to target URL: <a href="/cookies">/cookies</a>. If not click the link.PS C:\kod\ogren\ders26> curl.exe -b cerez.txt https://httpbin.org/cookies{ "cookies": {}}Sunucu çerezi kendisi silemez, çünkü çerez senin bilgisayarında durur. Onun yerine aynı adla boş bir çerez gönderir ve son kullanma tarihini geçmişe, 1970'e çeker; Max-Age=0 da "ömrü sıfır saniye" der. curl, tarayıcı gibi, süresi geçmiş çerezi atar ve cerez.txt'ye artık yazmaz. 4. adımda sunucuya hiç çerez gitmedi: oturum kapandı.
Sınır durumu: 3. adımda -c cerez.txt'yi unutursan silme talimatı yine gelir, ama curl onu dosyaya işlemez. 4. adım o zaman hâlâ "oturum": "x9k2" gösterir. Tarayıcı bu işi kendiliğinden yapar; curl'de her şeyi sen söylersin.
Güvenli JSON alıcı
jsonal.mjs adında bir dosyaya jsonAl(adres) adında bir async fonksiyon yaz:
- Yanıt başarısızsa, mesajı
İstek başarısız: 403 Forbiddenbiçiminde (kod ve açıklama) olan bir hata fırlatsın. - Durum
204ise gövde olmadığı içinnulldöndürsün. Content-TypeJSON değilse, mesajıJSON bekleniyordu, gelen tür: text/html; charset=utf-8biçiminde olan bir hata fırlatsın.- Bunların hiçbiri değilse JSON'u okuyup döndürsün.
Sonra aşağıdaki beş adresi sırayla dene. Her biri için ya Tamam: ve gelen değerin türünü (gövde yoksa gövde yok) ya da Olmadı: ve hatanın mesajını yazdır.
https://ddragon.leagueoflegends.com/realms/tr.jsonhttps://httpbin.org/status/204https://httpbin.org/htmlhttps://httpbin.org/status/429https://ddragon.leagueoflegends.com/api/Versions.jsonBeklenen çıktı:
Tamam: objectTamam: gövde yokOlmadı: JSON bekleniyordu, gelen tür: text/html; charset=utf-8Olmadı: İstek başarısız: 429 TOO MANY REQUESTSOlmadı: İstek başarısız: 403 Forbiddenİpucu 1
Sıra önemli: önce yanit.ok, sonra 204, en son tür. 204'te ok zaten true.
İpucu 2
Türü yanit.headers.get("content-type") ile al; başlık yoksa null gelir, ?? "" ile boş metne çevir. Sonra includes("application/json").
İpucu 3
Çözümü göster
async function jsonAl(adres) { const yanit = await fetch(adres) if (!yanit.ok) { throw new Error("İstek başarısız: " + yanit.status + " " + yanit.statusText) } if (yanit.status === 204) { return null } const tur = yanit.headers.get("content-type") ?? "" if (!tur.includes("application/json")) { throw new Error("JSON bekleniyordu, gelen tür: " + tur) } return await yanit.json()} const adresler = [ "https://ddragon.leagueoflegends.com/realms/tr.json", "https://httpbin.org/status/204", "https://httpbin.org/html", "https://httpbin.org/status/429", "https://ddragon.leagueoflegends.com/api/Versions.json"] for (const adres of adresler) { try { const veri = await jsonAl(adres) if (veri === null) { console.log("Tamam: gövde yok") } else { console.log("Tamam:", typeof veri) } } catch (hata) { console.log("Olmadı:", hata.message) }}Tamam: objectTamam: gövde yokOlmadı: JSON bekleniyordu, gelen tür: text/html; charset=utf-8Olmadı: İstek başarısız: 429 TOO MANY REQUESTSOlmadı: İstek başarısız: 403 ForbiddenSınır durumu: httpbin'in 204 yanıtında Content-Type: text/html başlığı var ama gövde yok. Tür denetimini 204'ten önce yapsaydın bu başarılı yanıtı hata sayardın; sıranın önemi bu. 429'un açıklaması büyük harfli geliyor, çünkü onu sunucu yazıyor; programın sayıya güvenmesi bu yüzden.
Kendini sına
Soru 1
Bu yanıtın başı ne söylüyor? Yeni kaydın adresi ne?
HTTP/1.1 201 CreatedContent-Type: application/jsonLocation: /maclar/43Cevabı göster
201 Created: istek başarılı ve yeni bir kayıt oluşturuldu; büyük olasılıkla bir POST'un cevabı. Yeni kaydın adresi Location başlığında: /maclar/43. Gövde JSON.
Soru 2
401 mi, 403 mü? (a) Giriş yapmadan /maclarim adresine istek attın. (b) Giriş yaptın ama başka bir oyuncunun maçını silmeye çalıştın.
Cevabı göster
(a) 401 Unauthorized: kim olduğunu söylemedin. (b) 403 Forbidden: kim olduğun belli, ama o kayıt üzerinde iznin yok. Bazı API'ler başkasının kaydının varlığını bile belli etmemek için 404 döndürür; bu da yaygın bir tercih.
Soru 3
Bir API'nin belgesinde "anahtarınızı Authorization başlığında, Bearer ile gönderin" yazıyor. Anahtarın abc123 olsun. https://api.example.com/maclarim adresine bu isteği curl.exe ile nasıl atarsın? Başlığı unutursan hangi durum kodunu beklersin?
Cevabı göster
curl.exe https://api.example.com/maclarim -H "Authorization: Bearer abc123". -H isteğe bir başlık ekler; değeri Bearer, bir boşluk ve anahtardan oluşur. Başlığı unutursan 401 Unauthorized gelir: sunucu kim olduğunu bilmiyor. Anahtar yanlışsa da 401 gelir, ama mesaj farklıdır (GitHub'daki Bad credentials gibi). Gerçek bir anahtarı komutun içine yazmaktan kaçın; terminal geçmişinde kalır.
Soru 4
Sunucudaki kayıt { "isim": "Ahri", "hp": 100, "mana": 300 }. PUT /sampiyonlar/ahri isteğini { "hp": 120 } gövdesiyle gönderdin. Kayıt ne olur? Aynı gövdeyi PATCH ile gönderseydin?
Cevabı göster
PUT kaydı gövdeyle tümüyle değiştirir: kayıt { "hp": 120 } olur, isim ve mana gider. Pek çok API bunun yerine "eksik alan var" diye 400 döndürür. PATCH yalnızca gövdedeki alanı değiştirir: { "isim": "Ahri", "hp": 120, "mana": 300 }.
Soru 5
Hatayı bul: Windows PowerShell 5.1'de bu komut neden çalışmaz, nasıl düzeltirsin?
curl -X DELETE https://httpbin.org/anything/maclar/7Cevabı göster
Bu PowerShell'de curl, Invoke-WebRequest'in takma adı; o komutun -X diye bir seçeneği yok. PowerShell şu hatayı verir: Invoke-WebRequest : A parameter cannot be found that matches parameter name 'X'. Düzeltmek için .exe ekle: curl.exe -X DELETE https://httpbin.org/anything/maclar/7.
Soru 6
Bu adreslerden hangileri REST kurallarına uymuyor, neden?
GET /sampiyonlar?rol=destekPOST /yeniMacEkleGET /maclar/12/silDELETE /maclar/12GET /Maclar.jsonCevabı göster
GET /sampiyonlar?rol=destekuygun: koleksiyon ve sorgu dizesinde süzme.POST /yeniMacEkleuymuyor: adreste fiil var. DoğrusuPOST /maclar.GET /maclar/12/silen tehlikelisi:GEThiçbir şeyi değiştirmemeli; bir bot ya da bağlantı önizlemesi bu adrese bakınca maç silinir. DoğrusuDELETE /maclar/12.DELETE /maclar/12uygun.GET /Maclar.jsonuymuyor: büyük harf ve uzantı. DoğrusuGET /maclar.
Soru 7
httpbin'e curl.exe ile bir gövde gönderdin. Yansımada "json": null var ve gövden "form" alanının içinde duruyor. Ne unuttun?
Cevabı göster
Content-Type başlığını. -d ile gönderilen gövdeyi curl, başlık yazmazsan application/x-www-form-urlencoded olarak işaretler ve sunucu onu form sanır. Düzeltmek için -H "Content-Type: application/json" ekle.
Soru 8
HTTP sunucusu bir önceki isteği hatırlamaz. O hâlde bir siteye giriş yaptıktan sonra sayfa değiştirdiğinde sunucu seni nasıl tanır? Bilgiyi kim saklar, her istekte kim gönderir?
Cevabı göster
Oturum çereziyle. Giriş başarılı olunca sunucu bir oturum kimliği üretir, onu kendi oturum kaydına yazar ve yanıtta Set-Cookie başlığıyla tarayıcıya verir. Tarayıcı çerezi saklar ve o siteye attığı her istekte Cookie başlığıyla kendiliğinden geri gönderir. Sunucu çerezdeki kimliği kendi kaydında arar ve seni tanır. Çerezi ele geçiren biri de senin yerine geçebilir; korumasını Ders 31'de göreceksin.
Kayıt noktası
cerez.txt bir oturum anahtarı taşıyabilecek türden bir dosya; Git'e girmemeli. C:\kod\ogren\.gitignore dosyasını aç (Ders 7'de oluşturmuştun) ve en alta şu satırı ekle:
cerez.txtSonra terminalde bir üst klasöre, C:\kod\ogren'e çık:
PS C:\kod\ogren\ders26> cd ..Bir sonraki derste masanın öbür tarafına geçiyorsun: node:http ile kendi sunucunu yazacak, bu derste gönderdiğin isteklere bu kez kendin cevap vereceksin.