HTTP Modülü ile Node.js'te Web Sunucusu Nasıl Oluşturulur
Giriş
Tarayıcınızda bir web sayfasını görüntülediğinizde, internetteki başka bir bilgisayara bir istek gönderiyorsunuz ve bu bilgisayar size yanıt olarak web sayfasını sunuyor. İnternet üzerinden iletişim kurduğunuz bu bilgisayara web sunucusu denir. Bir web sunucusu, tarayıcınız gibi bir istemciden HTTP istekleri alır ve bir HTML sayfası veya bir API’den JSON gibi HTTP yanıtları sağlar.
Node.js http modülü, harici bağımlılıklara gerek kalmadan bir web sunucusu oluşturmak için ihtiyacınız olan her şeyi sağlar. Gelen istekleri işler, HTTP başlıklarını ayrıştırır, bağlantıları yönetir ve yanıtları gönderir—tüm bunları basit JavaScript API'leri aracılığıyla yapar.
http modülü, HTTP sunucuları ve istemcileri oluşturmak için Node.js’in yerleşik çözümüdür. Express.js gibi çerçevelerin aksine, Node.js çekirdeğinin bir parçasıdır, yani kurulum veya bağımlılık gerekmez. Bu, onu hafif uygulamalar, HTTP temellerini öğrenmek veya istek yönetimi üzerinde tam kontrol sahibi olmak istediğiniz durumlar için ideal kılar.
Bir sunucunun bir web sayfası döndürebilmesi için çok sayıda yazılım devreye girer. Bu yazılımlar genellikle iki kategoriye ayrılır: frontend ve backend. Ön uç kodu, bir web sitesinin içeriğinin nasıl sunulduğu ile ilgilenir; örneğin bir gezinme çubuğunun rengi ve metin stilini. Arka uç kodu ise verilerin nasıl değiş tokuş edildiği, işlendiği ve saklandığıyla ilgilenir. Tarayıcınızdan gelen ağ isteklerini işleyen veya veritabanı ile iletişim kuran kodlar öncelikle arka uç kodu tarafından yönetilir.
Node.js, geliştiricilerin JavaScript'i arka uç kodu yazmak için kullanmasına olanak tanır, oysa geleneksel olarak JavaScript tarayıcıda ön uç kodu yazmak için kullanılırdı. Ön uç ve arka ucu birlikte kullanmak, bir web sunucusu oluşturmak için gereken çabayı azaltır, bu da Node.js'in arka uç kodu yazmak için popüler bir tercih olmasının başlıca nedenlerinden biridir.
Bu eğitim, Node.js'e dahil olan http modülünü kullanarak üretime hazır web sunucuları nasıl oluşturacağınızı göstermektedir. Yönlendirmeyi işleyen, istek gövdelerini ayrıştıran, statik dosyaları sunan, düzgün hata yönetimi uygulayan ve hem CommonJS hem de ES Modül sözdizimi ile çalışan sunucular oluşturacaksınız.
Linux-Console.net Uygulama Platformu ile Node.js uygulamalarını dağıtmayı basitleştirin. Node'u GitHub'dan sadece birkaç dakika içinde dağıtın.
Ana Noktalar
Dalışa başlamadan önce, işte başarmış olacağınız şeyler:
Framework kullanmadan Node.js çekirdek
httpmodülünü kullanarak HTTP sunucuları oluşturun:
Sadece Node.js’in çekirdekhttpmodülünü kullanarak tamamen işlevsel bir HTTP web sunucusu nasıl başlatacağınızı öğreneceksiniz. Bu, harici bağımlılıkları veya framework’leri kullanmaktan kaçınmak anlamına gelir ve sunucuların düşük seviyede nasıl çalıştığını daha derinlemesine anlamanızı sağlar, ayrıca kullanım senaryolarınız için daha hafif ve esnek uygulamalar yapmanıza olanak tanır.Farklı içerik türlerini (JSON, HTML, CSV, statik dosyalar) uygun başlıklarla işleyin:
Birden çok içerik türüyle çalışmayı öğreneceksiniz; web sayfaları için HTML, API'ler için JSON, CSV dosyaları veya resim ve betikler gibi statik varlıkları sunma gibi. Tarayıcıların ve istemcilerin yanıtlarınızı doğru bir şekilde yorumlamasını sağlamak içinContent-Typegibi HTTP başlıklarını nasıl doğru şekilde ayarlayacağınızı anlayacaksınız, böylece kesintisiz kullanıcı deneyimleri elde edebilirsiniz.İstek URL'si analizini ve switch ifadelerini kullanarak yönlendirme uygulayın:
Yönlendirme, web sunucuları için temeldir ve farklı URL’ler ve uç noktalar için farklı mantıklar çalıştırmanıza olanak tanır. Gelen istek URL’lerini analiz etme tekniklerini ve sunucu mantığınızı yapılandırma yöntemlerini (örneğin switch ifadeleri ile) göreceksiniz; böylece yollar ve HTTP yöntemlerine göre özel yanıtlar sağlayabilir ve daha büyük çerçevelerin arka planda yaptığı temel işlevleri simüle edebilirsiniz.POST isteklerini işleyin, istek gövdelerini ayrıştırın ve farklı içerik türlerini yönetin:
Kullanıcı tarafından gönderilen verileri yönetmek, herhangi bir etkileşimli uygulama için anahtardır. POST isteklerini nasıl işleyeceğinizi, istek gövdesini ham veri, JSON veya diğer formatlarda okuma ve ayrıştırma da dahil olmak üzere öğreneceksiniz. Bu bölüm, gelen verileri tamponlama, güvenli bir şekilde ayrıştırma ve alınan yüklemeye göre yanıt verme konularını kapsar; kullanıcı etkileşimi veya API geliştirme için temel yapı taşlarını sağlar.Dosya sisteminden statik dosyaları verimli bir şekilde sunun:
Birçok web sunucusu, statik varlıkları—resimler, stil sayfaları, JavaScript dosyaları—doğrudan kullanıcılara sunmak zorundadır. Dosyaları diskten nasıl okuyacağınızı ve HTTP üzerinden verimli bir şekilde nasıl göndereceğinizi, dosya yollarını güvenlik sorunlarından kaçınacak şekilde yönetmeyi ve daha büyük dosyalar için performansı artırmak amacıyla dosya akışı gibi optimizasyonları keşfedeceksiniz.Uygun HTTP durum kodları ve günlük kaydı ile hata yönetimini uygulayın:
Güçlü bir sunucu hataları düzgün bir şekilde yönetebilmelidir. Hataları—örneğin hatalı istekler veya eksik dosyalar—standart HTTP durum kodları olan 400, 404 ve 500 kullanarak nasıl tespit edip, kaydedip ve yanıt verebileceğinizi göreceksiniz. Ayrıca, sorunları takip edilebilir hale getirmek ve kullanıcıların hassas bilgileri sızdırmadan bilgilendirici hata mesajları almasını sağlamak için temel günlük kaydı uygulamalarını öğreneceksiniz.-
Hem CommonJS (
require) hem de ES Modülleri (import) sözdizimi ile modern JavaScript kullanın:
Node.js iki modül sistemini destekler: CommonJS ve ES Modülleri. Hemrequire()hem deimportifadelerini kullanmayı uygulayacak, her bir stili ne zaman ve nasıl kullanacağınızı anlayacaksınız. Bu, sadece farklı Node.js projeleri arasında uyumluluğu sağlamakla kalmaz, aynı zamanda modern, temiz ve sürdürülebilir JavaScript kodu yazmanıza da yardımcı olur. Dosya önbellekleme, hata kurtarma ve güvenlik önlemleri dahil üretim en iyi uygulamalarını uygulayın:
Son olarak, gerçek dünya ortamlarında Node.js web sunucularını dağıtırken temel en iyi uygulamalara değineceksiniz. Bu, performans için dosya önbelleklemeden yararlanmayı, sunucunuzu dayanıklı tutmak için hata kurtarma stratejilerini uygulamayı ve uygulamalarınızı ve kullanıcılarınızı korumak için temel güvenlik önlemlerini (örneğin, girdi temizleme ve uygun HTTP başlıkları) uygulamayı içerir.
HTTP Modülünü Anlamak
http modülü, Node.js’in HTTP iletişimi için düşük seviyeli arayüzüdür. Şunları sağlar:
- Sunucu oluşturma:
http.createServer()bir HTTP sunucu örneği oluşturur - İstek işleme: Gelen istekler
IncomingMessagenesneleri ile temsil edilir - Yanıt yönetimi: Giden yanıtlar
ServerResponsenesnelerini kullanır - Akış desteği: Hem istekler hem de yanıtlar akış şeklindedir, bu da verimli veri transferini sağlar
Neden Express Yerine HTTP Modülünü Kullanmalı? Express.js kullanışlı soyutlamalar sunarken, yerleşik http modülü size tam kontrol, daha küçük paket boyutları ve HTTP temellerinin derinlemesine anlaşılmasını sağlar. Bu, çerçevelerin üzerine inşa edildiği temeldir. Basit API’ler, mikro hizmetler veya öğrenme amaçları için genellikle ihtiyacınız olan tek şey budur.
Modül, olay odaklıdır: bir istek geldiğinde, sunucu bir 'request' olayı yayar ve isteğinizi dinleyen fonksiyonu çağırır. Bu tasarım, Node.js’in olay döngüsü aracılığıyla tek bir iş parçacığında birçok eşzamanlı bağlantıyı verimli bir şekilde işlemesini sağlar.
Anlaşılması gereken temel kavramlar:
- Bloklamayan G/Ç:
httpmodülü, her istek için ayrı bir iş parçacığı oluşturmadan birden fazla bağlantıyı yönetmek için Node.js’in olay döngüsünü kullanır. Bu, I/O ağırlıklı işlemler için onu son derece verimli kılar. - Akışlar: İstek ve yanıt nesneleri Node.js akışlarından türetilir. Bu, büyük verilerin tümünü belleğe yüklemeden parçalar halinde işlenebileceği anlamına gelir—dosya yüklemelerini yönetmek veya büyük dosyaları sunmak için kritik öneme sahiptir.
- Başlıklar ve durum kodları: Bu modül, HTTP başlıkları (
res.setHeader()) ve durum kodları (res.writeHead()) üzerinde doğrudan kontrol sağlar, böylece hassas HTTP anlamlarını yönetmenize olanak tanır. - Sihrî yönlendirme yok: Frameworklerin aksine, yerleşik bir yönlendirme sistemi yok. Yanıt vermek için
req.urlvereq.method'i manuel olarak analiz edersiniz—bu, yönlendirmenin aslında nasıl çalıştığını tamamen şeffaf bir şekilde görmenizi sağlar.
Sen http.createServer() çağırdığında, Node.js arka planda gelen bağlantıları dinleyen, HTTP protokolü mesajlarını analiz eden ve kodunuzun ele alabileceği olaylar yayınlayan bir TCP sunucusu oluşturur. Bu düşük seviyeli işlemi anlamak, sorunları çözmenize ve bilinçli mimari kararlar almanıza yardımcı olur.
Önkoşullar
Geliştirme makinenizde Node.js'in yüklü olduğundan emin olun. Bu eğitim Node.js 20.x veya 22.x LTS (önerilen) sürümleri ile çalışır. macOS veya Ubuntu'ya Node.js yüklemek için, macOS üzerinde Node.js'i Yükleme ve Yerel Geliştirme Ortamı Oluşturma veya Ubuntu'da Node.js Nasıl Yüklenir başlıklarındaki adımları izleyin.
Node.js platformu kutudan çıktığı gibi web sunucuları oluşturmayı destekler. Node.js'in temellerine aşina olmak faydalıdır. Node.js'te İlk Programınızı Nasıl Yazıp Çalıştıracağınıza dair rehberimizi inceleyin.
Asenkron programlama bazı bölümlerde kullanılır. Eğer asenkron kodlara veya
fsmodülüne yenilseniz, Node.js'te Asenkron Kod Nasıl Yazılır konusuna bakın.-
Üretim odaklı bölümler için, dağıtım rehberini uygularken Linux süreç yönetimi (systemd/PM2) ve ters vekil sunucular (Nginx gibi) hakkında bilgi sahibi olmak faydalı olacaktır.
Adım 1 — Temel Bir HTTP Sunucusu Oluşturma
Herhangi bir isteğe düz metinle yanıt veren minimal bir HTTP sunucusu oluşturun. Bu temel, Express kullanıp kullanmadığınıza veya sıfırdan yapmanıza bakılmaksızın geçerlidir. Node.js http.createServer() çağrıldığında, bir portta dinleyen bir TCP sunucusu oluşturur. Her gelen bağlantı, istek dinleyici fonksiyonunuzu tetikler.
Proje klasörünüzü oluşturun:
mkdir first-servers
cd first-serversŞimdi, kodu içerecek dosyayı oluşturun:
touch hello.jsDosyayı bir metin düzenleyicide açın. Terminalde mevcut olduğu için nano kullanacağız:
nano hello.jsTüm Node.js kurulumlarıyla standart olarak gelen http modülünü yükleyerek başlıyoruz. Aşağıdaki satırı hello.js dosyasına ekleyin:
const http = require("http");http modülü, daha sonra göreceğimiz sunucuyu oluşturma işlevini içerir. Node.js'teki modüller hakkında daha fazla bilgi edinmek isterseniz, Node.js Modülü Nasıl Oluşturulur makalemize göz atabilirsiniz.
Bir sonraki adımımız, sunucumuzun bağlı olacağı ana bilgisayar ve port olmak üzere iki sabit tanımlamak olacak:
...
const host = 'localhost';
const port = 8000;Daha önce belirtildiği gibi, web sunucuları tarayıcılardan ve diğer istemcilerden gelen isteklere yanıt verir. Bir alan adını girerek bir web sunucusu ile etkileşimde bulunabiliriz; bu alan adı bir DNS sunucusu tarafından bir IP adresine çevrilir. Bir IP adresi, internetteki gibi bir ağdaki bir makineyi tanımlayan benzersiz bir sayı dizisidir. Alan adı kavramları hakkında daha fazla bilgi için DNS Terimleri, Bileşenleri ve Kavramlarına Giriş makalemize göz atabilirsiniz.
localhost değeri, bilgisayarların kendilerini tanımlamak için kullandığı özel bir özel adresdir. Genellikle dahili IP adresi 127.0.0.1 ile eşdeğerdir ve yalnızca yerel bilgisayar tarafından kullanılabilir; katıldığımız yerel ağlara veya internete erişim sağlamak için kullanılamaz.
Port, sunucuların IP adresimize bir uç nokta veya "kapı" olarak kullandığı bir numaradır. Örneğimizde web sunucumuz için 8000 portunu kullanacağız. 8080 ve 8000 portları genellikle geliştirme ortamlarında varsayılan portlar olarak kullanılır ve çoğu durumda geliştiriciler HTTP sunucuları için diğer portlar yerine bu portları kullanır.
Sunucumuzu bu host ve porta bağladığımızda, yerel bir tarayıcıda http://localhost:8000 adresini ziyaret ederek sunucumuza ulaşabileceğiz.
Hadi Node.js'te istek dinleyicisi olarak adlandırdığımız özel bir fonksiyon ekleyelim. Bu fonksiyon, gelen bir HTTP isteğini işlemek ve bir HTTP cevabı döndürmek için tasarlanmıştır. Bu fonksiyonun iki argümanı olmalıdır: bir istek nesnesi ve bir yanıt nesnesi. İstek nesnesi, gelen HTTP isteğinin tüm verilerini yakalar. Yanıt nesnesi ise sunucu için HTTP yanıtları döndürmek amacıyla kullanılır.
İlk sunucumuzun, birisi ona eriştiğinde bu mesajı döndürmesini istiyoruz: "Benim ilk sunucum!".
Şimdi o fonksiyonu ekleyelim:
...
const requestListener = function (req, res) {
res.writeHead(200);
res.end("My first server!");
};Fonksiyon genellikle yaptığı iş temel alınarak adlandırılır. Örneğin, eğer bir istek dinleyici fonksiyon oluşturup kitap listesi döndüreceksek, bunu muhtemelen listBooks() olarak adlandırırdık. Bu bir örnek durum olduğundan, genel isim olan requestListener ismini kullanacağız.
Node.js'teki tüm istek dinleyici fonksiyonları iki argüman kabul eder: req ve res (istersek bunları farklı isimlendirebiliriz). Kullanıcının gönderdiği HTTP isteği, bir Request nesnesi içerisinde yakalanır ve bu, ilk argüman olan req'ye karşılık gelir. Kullanıcıya geri gönderdiğimiz HTTP yanıtı ise, ikinci argüman olan res içindeki Response nesnesi ile etkileşime girilerek oluşturulur.
İlk satır res.writeHead(200); yanıtın HTTP durum kodunu ayarlar. HTTP durum kodları, bir HTTP isteğinin sunucu tarafından ne kadar iyi işlendiğini gösterir. Bu durumda, 200 durum kodu "OK" anlamına gelir. Web sunucularınızın döndürebileceği çeşitli HTTP kodlarını ve bunların anlamlarını öğrenmekle ilgileniyorsanız, Ortak HTTP Hata Kodlarını Gidermek İçin Nasıl Yapılır rehberimiz iyi bir başlangıç noktasıdır.
Fonksiyonun bir sonraki satırı, res.end("My first server!");, HTTP yanıtını istekte bulunan istemciye geri yazar. Bu fonksiyon, sunucunun döndürmesi gereken herhangi bir veriyi geri döndürür. Bu durumda, metin verisi döndürülüyor.
Son olarak, artık sunucumuzu oluşturabilir ve istek dinleyicimizi kullanabiliriz:
...
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});CTRL+X tuşlarına basarak nano'yu kaydedip çıkın.
İlk satırda, http modülünün createServer() fonksiyonu aracılığıyla yeni bir server nesnesi oluşturuyoruz. Bu sunucu HTTP isteklerini kabul eder ve bunları requestListener() fonksiyonumuza iletir.
Sunucumuzu oluşturduktan sonra, onu bir ağ adresine bağlamamız gerekir. Bunu server.listen() yöntemi ile yaparız. Bu yöntem üç argüman alır: port, host ve sunucu dinlemeye başladığında tetiklenen bir geri çağırma fonksiyonu.
Bu argümanların tümü isteğe bağlıdır, ancak bir web sunucusunun hangi portu ve ana bilgisayarı kullanmasını istediğimizi açıkça belirtmek iyi bir fikirdir. Web sunucularını farklı ortamlara dağıtırken, çalıştığı portu ve ana bilgisayarı bilmek, yük dengeleme veya bir DNS takma adı kurmak için gereklidir.
Geri çağırma fonksiyonu, sunucunun bağlantıları dinlemeye başladığını anlayabilmemiz için bir mesajı konsolumuza kaydeder.
İşte her bir kısmı açıklayan satır içi yorumlarla birlikte tam hello.js dosyası:
// Import the built-in http module - no installation needed
const http = require("http");
// Define where the server will listen
const host = 'localhost'; // Only accessible from this machine
const port = 8000; // Common development port
// Request listener: handles every incoming HTTP request
const requestListener = function (req, res) {
// Set HTTP status code to 200 (OK)
res.writeHead(200);
// Send the response body and close the connection
res.end("My first server!");
};
// Create the server with our request listener
const server = http.createServer(requestListener);
// Start listening for connections
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});Bu kod, 20 satırın altında işlevsel bir HTTP sunucusu oluşturur. Bir istemci her bağlandığında—URL veya HTTP yöntemi ne olursa olsun—sunucu "İlk sunucum!" ve 200 durum kodu ile yanıt verir.
Not: requestListener() req nesnesini kullanmasa da, yine de fonksiyonun ilk argümanı olmalıdır.
15 satırdan az kodla artık bir web sunucumuz var. Hadi bunu çalışırken görelim ve programı çalıştırarak uçtan uca test edelim:
node hello.jsKonsolda, bu çıktıyı göreceğiz:
Server is running on http://localhost:8000İmlecin kaybolduğuna dikkat edin. Bunun nedeni, bir Node.js sunucusunun uzun süre çalışan bir süreç olmasıdır. Sadece, çökmesine ve kapanmasına neden olan bir hatayla karşılaşırsa ya da sunucuyu çalıştıran Node.js sürecini durdurursak çıkış yapar.
Ayrı bir terminal penceresinde, cURL kullanarak sunucuyla iletişim kuracağız; cURL, verileri bir ağ üzerinden alıp göndermek için kullanılan bir CLI aracıdır. Çalışan sunucumuza bir HTTP GET isteği yapmak için komutu girin:
curl http://localhost:8000Biz ENTER tuşuna bastığımızda, terminalimiz aşağıdaki çıktıyı gösterecek:
My first server!Artık bir sunucu kurduk ve ilk sunucu yanıtımızı aldık.
Sunucumuzu test ettiğimizde ne olduğunu adım adım inceleyelim. cURL kullanarak sunucuya GET isteği gönderdik: http://localhost:8000. Node.js sunucumuz bu adresten gelen bağlantıları dinledi. Sunucu bu isteği requestListener() fonksiyonuna iletti. Fonksiyon, 200 durum koduyla birlikte metin verisi döndürdü. Sunucu ardından bu yanıtı cURL’e geri gönderdi ve cURL mesajı terminalimizde görüntüledi.
Üretim ortamında genellikle localhost'a bağlanmazsınız; her ağ arayüzünden bağlantıları kabul etmek için 0.0.0.0'a bağlanırsınız. Ancak, yerel geliştirme için localhost daha güvenlidir ve geliştirme sunucunuza dış erişimi engeller. Ayrıca, 8000 portu zaten kullanılıyorsa, bir EADDRINUSE hatası alırsınız—sadece farklı bir port seçin veya o portu kullanan işlemi lsof -i :8000 (macOS/Linux) ile durdurun.
Devam etmeden önce, çalışan sunucumuzu CTRL+C tuşlarına basarak durduralım. Bu, sunucumuzun çalışmasını keser ve bizi komut satırı istemine geri getirir.
Ziyaret ettiğimiz çoğu web sitesinde veya kullandığımız API'lerde, sunucu yanıtları nadiren düz metin şeklindedir. Yaygın yanıt formatları olarak HTML sayfaları ve JSON verileri alırız. Bir sonraki adımda, webde karşılaştığımız yaygın veri formatlarında HTTP yanıtlarının nasıl döndürüleceğini öğreneceğiz.
Node.js'ten (istemci tarafı) HTTP isteği yapmayı öğrenmek istiyorsanız, Node.js'te Core HTTP ile HTTP İstemcisi Nasıl Oluşturulur rehberimize göz atın.
Adım 2 — Farklı Türde İçerik Döndürme
Bir web sunucusundan döndürdüğümüz yanıt çeşitli formatlarda olabilir. Daha önce JSON ve HTML'den bahsedilmişti ve ayrıca XML ve CSV gibi diğer metin formatlarını da döndürebiliriz. Son olarak, web sunucuları PDF'ler, sıkıştırılmış dosyalar, ses ve video gibi metin dışı verileri de döndürebilir.
Bu makalede, az önce geri döndürdüğümüz düz metnin yanı sıra, aşağıdaki veri türlerini nasıl geri döndüreceğinizi de öğreneceksiniz:
- JSON
- CSV
- HTML
Üç veri türü de metin tabanlıdır ve web üzerinde içerik sunmak için popüler formatlardır. Birçok sunucu tarafı geliştirme dili ve aracı, bu farklı veri türlerini döndürmeyi destekler. Node.js bağlamında iki şey yapmamız gerekiyor:
- HTTP yanıtlarımızda
Content-Typebaşlığını uygun değeriyle ayarlayın. - Verilerin doğru formatta gelmesini
res.end()ile sağlayın.
Bunu bazı örneklerle uygulamada görelim. Bu bölümde ve ilerleyen bölümlerde yazacağımız kod, daha önce yazdığımız koda birçok yönden benziyor. Çoğu değişiklik requestListener() fonksiyonu içinde yer alıyor. Gelecek bölümleri takip etmeyi kolaylaştırmak için bu “şablon kod” ile dosyalar oluşturalım.
Yeni bir dosya oluşturun ve adına html.js verin. Bu dosya daha sonra bir HTTP yanıtında HTML metni döndürmek için kullanılacak. Şablon kodunu buraya koyacağız ve farklı türleri döndüren diğer sunuculara kopyalayacağız.
Terminalde, aşağıdakini girin:
touch html.jsŞimdi bu dosyayı bir metin düzenleyicide açın:
nano html.js“Şablon kodunu” kopyalayalım. Bunu nano'ya girin:
const http = require("http");
const host = 'localhost';
const port = 8000;
const requestListener = function (req, res) {};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});CTRL+X ile html.js'yi kaydedip çıkın, ardından terminale dönün.
Şimdi bu dosyayı iki yeni dosyaya kopyalayalım. İlk dosya, HTTP yanıtında CSV verilerini döndürmek için olacak:
cp html.js csv.jsİkinci dosya, sunucuda bir JSON yanıtı döndürecektir:
cp html.js json.jsKalan dosyalar daha sonraki alıştırmalar için olacak:
cp html.js htmlFile.js
cp html.js routes.jsArtık egzersizlerimize devam etmek için hazırız. JSON döndürerek başlayalım.
JSON Sunma
JavaScript Nesne Gösterimi, yaygın olarak JSON olarak adlandırılır, metin tabanlı bir veri alışverişi formatıdır. Adından da anlaşılacağı üzere JavaScript nesnelerinden türetilmiştir, ancak dil bağımsızdır; bu, sözdizimini çözebilen herhangi bir programlama dili tarafından kullanılabileceği anlamına gelir.
JSON, API'ler tarafından veri alıp göndermek için yaygın şekilde kullanılır. Popülerliği, XML gibi önceki veri alışverişi standartlarına kıyasla daha düşük veri aktarım boyutuna sahip olmasından ve programların bunları fazla çaba harcamadan çözümleyebilmesini sağlayan araçların bulunmasından kaynaklanmaktadır. JSON hakkında daha fazla bilgi edinmek isterseniz, JavaScript'te JSON ile Çalışma Rehberimizi okuyabilirsiniz.
json.js dosyasını nano ile açın:
nano json.jsJSON sunarken, Content-Type başlığını application/json olarak ayarlamalısınız. Bunu yapmazsanız, tarayıcılar ve API istemcileri yanıtı düz metin olarak değerlendirir ve bu da fetch().json()'in başarısız olmasına neden olur. Başlıkları writeHead() çağırmadan önce ayarlayın—başlıklar gönderildikten sonra değiştirilemez.
Yaygın hata: res.setHeader()'den önce res.writeHead(200) çağırmak, başlıkları kilitler. writeHead()'den sonra başlıkları ayarlamaya çalışırsanız, göz ardı edilirler. Her zaman önce setHeader()'i, sonra writeHead()'i çağırın.
requestListener() fonksiyonunu değiştirin:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "application/json");
res.writeHead(200);
res.end(`{"message": "This is a JSON response"}`);
};
...Durum kodu 200 başarıyı gösterir. Yanıt gövdesi geçerli JSON içerir.
JSON.stringify()'i elle yazılan stringler yerine neden kullanmalısınız? JSON'u elle yazmak hataya açık bir yöntemdir. Mesajınız tırnak işaretleri, yeni satırlar veya özel karakterler içeriyorsa, bunları manuel olarak kaçış karakteriyle yazmanız gerekir. JSON.stringify() bunu otomatik olarak halleder:
const data = {
message: "This is a JSON response",
timestamp: Date.now(),
note: 'He said "hello"' // Quotes automatically escaped
};
res.setHeader("Content-Type", "application/json");
res.writeHead(200);
res.end(JSON.stringify(data));Dairesel referanslara dikkat edin: Eğer nesneniz kendisine referans veriyorsa, JSON.stringify() şunu fırlatır: TypeError: Converting circular structure to JSON. Bu genellikle veritabanı sorgu sonuçlarında olur. Veri yapınızı her zaman doğrulayın.
Bir nesneyi doğrudan göndermeye çalışırsanız ne olur? http modülü yalnızca dizgeleri (string) veya Buffer'ları kabul eder. Eğer res.end({ message: "test" }) gibi bir şey denerseniz, Node.js şunu fırlatır: TypeError [ERR_INVALID_ARG_TYPE]: İlk argüman string veya Buffer türlerinden biri olmalıdır. Nesneleri önce JSON dizgelerine dönüştürmeniz gerekir.
Başlığın doğru ayarlandığını doğrulayın: Yanıt başlıklarını görmek için curl ile -i bayrağını kullanın:
curl -i http://localhost:8000Başlıklarda Content-Type: application/json görmelisiniz. Eğer text/plain görüyorsanız veya Content-Type hiç yoksa, ya writeHead()'den sonra setHeader() çağırmışsınızdır ya da tamamen unuttuğunuz anlamına gelir.
Yaygın hata ayıklama senaryosu: Eğer frontend JavaScript kodunuz fetch('/api').then(r => r.json()) çağırdığında undefined alıyorsa, başlıkları curl -i ile kontrol edin. Content-Type tam olarak application/json olmalıdır (bazı sıkı ayrıştırıcılar için büyük/küçük harf duyarlıdır).
CTRL+X tuşlarına basarak json.js dosyasını kaydedip çıkın. Şimdi, sunucuyu node komutuyla çalıştıralım:
node json.jsBaşka bir terminalde, cURL kullanarak sunucuya ulaşalım:
curl http://localhost:8000ENTER tuşuna bastığımızda, aşağıdaki sonucu göreceğiz:
{"message": "This is a JSON response"}Artık popüler API'lerle uygulama oluştururken yaptığımız gibi başarılı bir şekilde JSON yanıtı döndürdük. Çalışan sunucudan çıkmak için CTRL+C tuş kombinasyonunu kullandığınızdan emin olun ki standart terminal istemine geri dönebilelim. Şimdi, veri döndürmenin bir başka popüler formatına, CSV'ye bakalım.
CSV Sunma
Virgülle Ayrılmış Değerler (CSV) dosya formatı, genellikle tablo verilerini sağlamak için kullanılan bir metin standardıdır. Çoğu durumda, her satır bir yeni satır ile ayrılır ve satırdaki her öğe bir virgülle ayrılır.
Çalışma alanımızda, csv.js dosyasını bir metin düzenleyici ile açın:
nano csv.jsŞu satırları requestListener() fonksiyonumuza ekleyelim:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/csv");
res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
};
...Bu sefer, Content-Type başlığımız, değer text/csv olduğundan bir CSV dosyasının döndürüldüğünü gösteriyor. Eklediğimiz ikinci başlık ise Content-Disposition başlığıdır. Bu başlık, verilerin tarayıcıda veya ayrı bir dosya olarak nasıl görüntüleneceğini tarayıcıya bildirir.
CSV yanıtları döndürdüğümüzde, çoğu modern tarayıcı Content-Disposition başlığı ayarlanmamış olsa bile dosyayı otomatik olarak indirir. Ancak, bir CSV dosyası döndürürken bu başlığı yine de eklemeliyiz çünkü bu, CSV dosyasının adını belirlememize olanak tanır. Bu durumda, tarayıcıya bu CSV dosyasının bir ek olduğunu ve indirilmesi gerektiğini bildiririz. Daha sonra tarayıcıya dosyanın adının oceanpals.csv olduğunu belirtiriz.
HTTP yanıtına CSV verilerini yazalım:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/csv");
res.setHeader("Content-Disposition", "attachment;filename=oceanpals.csv");
res.writeHead(200);
res.end(`id,name,email\n1,Sammy Shark,shark@ocean.com`);
};
...200/OK durumunu CSV verisi ile döndürün. Virgül her sütundaki değerleri ayırır ve yeni satır karakteri (\n) satırları ayırır. İki satırımız var: başlık satırı ve bir veri satırı.
CSV biçimlendirme uyarısı: Yukarıdaki basit örnek çalışıyor, ancak üretim ortamında CSV oluştururken kaçış karakterleri gerekir. Verileriniz virgül içeriyorsa (örn. "Smith, John") veya tırnak işareti içeriyorsa, CSV formatını bozabilir. Doğru CSV kaçışı, değerleri tırnak içine alır ve iç tırnakları çiftler: "Smith, John" CSV’de "Smith, ""John""" hâline gelir. Üretim ortamı için csv-stringify gibi bir kütüphane kullanın veya değerleri düzgün bir şekilde kaçırarak ayrıştırma hatalarını önleyin.
Content-Disposition olmadan ne olur? Çoğu tarayıcı, Content-Type: text/csv ayarlandığında dosyayı indirir, ancak dosya adı URL yolu veya genel bir isim olabilir. attachment;filename=oceanpals.csv başlığı, tarayıcının dosyayı belirli bir isimle kaydetmesini sağlar. attachment olmadan, bazı tarayıcılar CSV'yi satır içinde göstermeye çalışır ve bu genellikle düzensiz görünür.
Bu sunucuyu tarayıcıda test edeceğiz. csv.js dosyasını kaydedin ve editörden CTRL+X ile çıkın.
Sunucuyu Node.js komutuyla çalıştırın:
node csv.jsBaşka bir Terminalde, cURL kullanarak sunucuya ulaşalım:
curl http://localhost:8000Konsol bunu gösterecek:
id,name,email
1,Sammy Shark,shark@ocean.comTarayıcımızda http://localhost:8000 adresine gidersek, bir CSV dosyası indirilecektir. Dosya adı oceanpals.csv olacaktır.
Çalışan sunucudan çıkmak için CTRL+C tuşlarına basın ve standart terminal istemine dönün.
JSON ve CSV döndürmeyi ele aldıktan sonra, API'ler için popüler olan iki durumu kapsadık. Şimdi insanların tarayıcıda görüntülediği web siteleri için veriyi nasıl döndürdüğümüze geçelim.
HTML Sunma
HTML (HyperText İşaretleme Dili), kullanıcıların bir web tarayıcısı aracılığıyla sunucumuzla etkileşimde bulunmasını istediğimizde en yaygın kullanılan formattır. Web içeriğini yapılandırmak için oluşturulmuştur. Web tarayıcıları HTML içeriğini görüntülemek için tasarlanmış olup, web sitelerimizin görünümünü değiştirmemize olanak tanıyan başka bir ön yüz web teknolojisi olan CSS ile eklediğimiz stilleri de gösterebilir.
Metin düzenleyicimizle html.js dosyasını tekrar açalım:
nano html.jsrequestListener() fonksiyonunu, bir HTML yanıtı için uygun Content-Type başlığını döndürecek şekilde değiştirin:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/html");
};
...Şimdi HTML içeriğini kullanıcıya geri dönelim. Vurgulanan satırları html.js dosyasına ekleyin, böylece şöyle görünsün:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(`<html><body><h1>This is HTML</h1></body></html>`);
};
...Önce HTTP durum kodunu ekliyoruz. Daha sonra geçerli HTML içeren bir string argüman ile response.end() fonksiyonunu çağırıyoruz. Tarayıcıda sunucumuza eriştiğimizde, içinde This is HTML bulunan bir başlık etiketi olan bir HTML sayfası göreceğiz.
Kaydetmek ve çıkmak için CTRL+X tuşlarına basalım. Şimdi, sunucuyu node komutuyla çalıştıralım:
node html.jsProgramımız başladığında Server is running on http://localhost:8000 mesajını göreceğiz.
Şimdi tarayıcıya gidin ve http://localhost:8000 adresini ziyaret edin. Sayfamız şöyle görünecek:
Çalışan sunucuyu CTRL+C ile durduralım ve standart terminal istemine dönelim.
HTML’in, Node.js programlarımız gibi sunucu tarafı kodundan ayrı bir dosyada yazılması yaygındır. Şimdi, HTML yanıtlarını dosyalardan nasıl döndürebileceğimize bakalım.
Adım 3 — Bir Dosyadan HTML Sayfası Sunma
Node.js'te HTML’i kullanıcıya string olarak sunabiliriz, ancak HTML dosyalarını yükleyip içeriklerini sunmamız tercih edilir. Bu şekilde, HTML dosyası büyüdükçe Node.js kodumuzda uzun stringler tutmamıza gerek kalmaz, kod daha özlü olur ve web sitemizin her yönü üzerinde bağımsız olarak çalışmamıza olanak sağlar. Bu "endişelerin ayrımı" birçok web geliştirme ortamında yaygındır, bu yüzden Node.js'te HTML dosyalarını nasıl yükleyeceğini bilmek iyi bir yöntemdir.
HTML dosyalarını sunmak için, HTML dosyasını fs modülü ile yüklüyor ve HTTP yanıtımızı yazarken bu veriyi kullanıyoruz.
Öncelikle, web sunucusunun döndüreceği bir HTML dosyası oluşturacağız. Yeni bir HTML dosyası oluşturun:
touch index.htmlŞimdi index.html dosyasını bir metin düzenleyicide açın:
nano index.htmlWeb sayfamız minimal olacak. Turuncu bir arka plana sahip olacak ve ortada bazı karşılama metinleri gösterecek. Bu kodu dosyaya ekleyin:
<!DOCTYPE html>
<head>
<title>My Website</title>
<style>
*,
html {
margin: 0;
padding: 0;
border: 0;
}
html {
width: 100%;
height: 100%;
}
body {
width: 100%;
height: 100%;
position: relative;
background-color: rgb(236, 152, 42);
}
.center {
width: 100%;
height: 50%;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-family: "Trebuchet MS", Helvetica, sans-serif;
text-align: center;
}
h1 {
font-size: 144px;
}
p {
font-size: 64px;
}
</style>
</head>
<body>
<div class="center">
<h1>Hello Again!</h1>
<p>This is served from a file</p>
</div>
</body>
</html>Bu tek web sayfası iki satır metin gösterir: Hello Again! ve Bu bir dosyadan sunuluyor. Satırlar sayfanın ortasında, üst üste olacak şekilde görünür. İlk satır bir başlık olarak görüntülenir, bu da büyük olacağı anlamına gelir. İkinci satır biraz daha küçük görünecektir. Tüm metin beyaz renkte olacak ve web sayfasının arka planı turuncu olacaktır.
Bu makale veya serinin kapsamı olmasa da, HTML, CSS ve diğer ön yüz web teknolojilerini öğrenmekle ilgileniyorsanız, Mozilla'nın Web ile Başlarken kılavuzuna göz atabilirsiniz.
HTML için bu kadar, bu yüzden dosyayı kaydedip CTRL+X ile çıkın. Artık sunucu koduna geçebiliriz.
Bu egzersiz için htmlFile.js üzerinde çalışacağız. Bunu bir metin düzenleyici ile açın:
nano htmlFile.jsBir dosya okumamız gerektiğinden, fs modülünü içe aktararak başlayalım:
const http = require("http");
const fs = require('fs').promises;
...Bu modül, HTML dosyasını yerinde yüklemek için kullanacağımız readFile() fonksiyonunu içeriyor. Modern JavaScript en iyi uygulamalarına uygun olarak promise varyantını içe aktarıyoruz. Promise kullanıyoruz çünkü bu sözdizimsel olarak callback’lerden daha kısa; eğer fs’yi sadece require('fs') ile atamış olsaydık callback kullanmak zorunda kalacaktık. Asenkron programlama en iyi uygulamaları hakkında daha fazla bilgi edinmek için Node.js'te Asenkron Kod Yazma rehberimizi okuyabilirsiniz.
Kullanıcı sistemimizi talep ettiğinde HTML dosyamızın okunmasını istiyoruz. Şimdi requestListener() fonksiyonunu dosyayı okumak üzere değiştirmeye başlayalım:
...
const requestListener = function (req, res) {
fs.readFile(__dirname + "/index.html")
};
...fs.readFile() metodunu dosyayı yüklemek için kullanıyoruz. Bunun argümanı __dirname + "/index.html" şeklindedir. Özel değişken __dirname, Node.js kodunun çalıştığı yerin mutlak yolunu içerir. Sonrasında /index.html ekleyerek daha önce oluşturduğumuz HTML dosyasını yükleyebiliriz.
Şimdi HTML sayfası yüklendikten sonra geri dönelim:
...
const requestListener = function (req, res) {
fs.readFile(__dirname + "/index.html")
.then(contents => {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(contents);
})
};
...Eğer fs.readFile() promise başarılı bir şekilde çözülürse, verisini döndürecektir. Bu durumu ele almak için then() metodunu kullanırız. contents parametresi HTML dosyasının verilerini içerir.
Öncelikle, istemciye HTML verisi döndüreceğimizi söylemek için Content-Type başlığını text/html olarak ayarlıyoruz. Daha sonra, isteğin başarılı olduğunu belirtmek için durum kodunu yazıyoruz. Son olarak, contents değişkeninde bulunan verilerle birlikte yüklediğimiz HTML sayfasını istemciye gönderiyoruz.
fs.readFile() yöntemi bazen başarısız olabilir, bu yüzden bir hata aldığımızda bu durumu ele almalıyız. Bunu requestListener() fonksiyonuna ekleyin:
...
const requestListener = function (req, res) {
fs.readFile(__dirname + "/index.html")
.then(contents => {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(contents);
})
.catch(err => {
res.writeHead(500);
res.end('Internal Server Error');
return;
});
};
...Dosyayı kaydedin ve CTRL+X ile nano'dan çıkın.
Bir sözleşme bir hata ile karşılaştığında reddedilir. Bu durumu catch() yöntemiyle ele alırız. Bu yöntem, durum kodunu 500 olarak ayarlar; bu, dahili bir hata ile karşılaşıldığını belirtir ve kullanıcı dostu bir hata mesajı gönderir. Üretim ortamında, kullanıcılara ham hata detaylarını asla göstermemelisiniz, çünkü bu hassas bilgilerin sızmasına yol açabilir.
Sunucumuzu node komutuyla çalıştırın:
node htmlFile.jsWeb tarayıcınızda http://localhost:8000 adresini ziyaret edin. Bu sayfayı göreceksiniz:
Artık sunucudan kullanıcıya bir HTML sayfası gönderdiniz. Çalışan sunucuyu CTRL+C ile durdurabilirsiniz. Bunu yaptığınızda terminal isteminin geri döndüğünü göreceksiniz.
Üretim ortamında böyle bir kod yazarken, her HTTP isteği aldığınızda bir HTML sayfası yüklemek istemeyebilirsiniz. Bu HTML sayfasının boyutu yaklaşık 800 bayttır, ancak daha karmaşık web siteleri megabaytlarca olabilir. Büyük dosyaların yüklenmesi biraz zaman alabilir. Siteniz yüksek trafik bekliyorsa, HTML dosyalarını başlangıçta yükleyip içeriklerini kaydetmek en iyisi olabilir. Yüklendikten sonra, sunucuyu kurabilir ve belirli bir adreste istekleri dinlemesini sağlayabilirsiniz.
Bu yöntemi göstermek için, sunucumuzu daha verimli ve ölçeklenebilir hale getirmek için nasıl yeniden düzenleyebileceğimize bakalım.
HTML'i Verimli Bir Şekilde Sunmak
Her istekte HTML yüklemek yerine, bu adımda HTML'i başta bir kez yükleyeceğiz. İstek, başlangıçta yüklediğimiz veriyi döndürecektir.
Terminalde, Node.js betiğini bir metin düzenleyici ile yeniden açın:
nano htmlFile.jsHadi requestListener() fonksiyonunu oluşturmadan önce yeni bir değişken ekleyerek başlayalım:
...
let indexFile;
const requestListener = function (req, res) {
...Bu programı çalıştırdığımızda, bu değişken HTML dosyasının içeriğini tutacaktır.
Şimdi, requestListener() fonksiyonunu yeniden ayarlayalım. Dosyayı yüklemek yerine artık indexFile içeriğini döndürecek:
...
const requestListener = function (req, res) {
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(indexFile);
};
...Sonraki adımda, dosya okuma mantığını requestListener() fonksiyonundan sunucumuzun başlatılma sürecine kaydırıyoruz. Sunucuyu oluştururken aşağıdaki değişiklikleri yapın:
...
const server = http.createServer(requestListener);
fs.readFile(__dirname + "/index.html")
.then(contents => {
indexFile = contents;
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});
})
.catch(err => {
console.error(`Could not read index.html file: ${err}`);
process.exit(1);
});Dosyayı kaydedin ve CTRL+X ile nano'dan çıkın.
Dosyayı okuyan kod, ilk denememizde yazdığımıza benzer. Ancak, dosyayı başarıyla okuduğumuzda artık içeriği global indexFile değişkenimize kaydediyoruz. Daha sonra sunucuyu listen() yöntemi ile başlatıyoruz. Önemli olan, sunucu çalıştırılmadan önce dosyanın yüklenmiş olmasıdır. Bu şekilde, requestListener() fonksiyonu bir HTML sayfası döndüreceğinden emin olabilir, çünkü indexFile artık boş bir değişken değildir.
Hata işleyicimiz de değişti. Dosya yüklenemezse hatayı yakalayıp konsolumuza yazdırıyoruz. Ardından sunucuyu başlatmadan Node.js programından exit() fonksiyonu ile çıkıyoruz. Bu sayede dosya okuma işleminin neden başarısız olduğunu görebilir, sorunu çözebilir ve ardından sunucuyu tekrar başlatabiliriz.
Şimdi kullanıcıya çeşitli türlerde veri döndüren farklı web sunucuları oluşturduk. Şimdiye kadar, neyin döndürüleceğini belirlemek için herhangi bir istek verisi kullanmadık. Node.js sunucusunda farklı rotalar veya yollar ayarlarken istek verilerini kullanmamız gerekecek, bu yüzden bir sonraki adımda bunların birlikte nasıl çalıştıklarına bakalım.
Adım 4 — Bir HTTP İsteği Nesnesi Kullanarak Rotaları Yönetme
Ziyaret ettiğimiz çoğu web sitesi veya kullandığımız API’ler genellikle birden fazla uç noktaya sahiptir, bu sayede çeşitli kaynaklara erişebiliriz. İyi bir örnek, bir kütüphanede kullanılabilecek bir kitap yönetim sistemi olurdu. Bu sistem sadece kitap verilerini yönetmekle kalmaz, aynı zamanda kataloglama ve arama kolaylığı için yazar verilerini de yönetir.
Kitaplar ve yazarlarla ilgili veriler ilişkili olsa da, bunlar iki farklı nesnedir. Bu durumlarda, yazılım geliştiricileri genellikle her nesneyi farklı uç noktalarda kodlayarak API kullanıcısına hangi tür veriyle etkileşimde olduklarını göstermek için bir yol sunarlar.
Küçük bir kütüphane için yeni bir sunucu oluşturalım; bu sunucu iki farklı türde veri döndürecek. Kullanıcı sunucumuzun /books adresine giderse, JSON formatında bir kitap listesi alacak. /authors adresine giderse, JSON formatında yazar bilgileri listesi alacak.
Şu ana kadar sunucu her isteğe aynı yanıtı veriyor. Birden fazla kaynağı desteklemek için verilerinizi değişkenlerde saklayın ve koşullu yönlendirme kullanın. Örneğin:
const books = JSON.stringify([
{ title: "The Alchemist", author: "Paulo Coelho", year: 1988 },
{ title: "The Prophet", author: "Kahlil Gibran", year: 1923 }
]);
const authors = JSON.stringify([
{ name: "Paulo Coelho", countryOfBirth: "Brazil", yearOfBirth: 1947 },
{ name: "Kahlil Gibran", countryOfBirth: "Lebanon", yearOfBirth: 1883 }
]);
const requestListener = function (req, res) {
res.setHeader("Content-Type", "application/json");
switch (req.url) {
case "/books":
res.writeHead(200);
res.end(books);
break;
case "/authors":
res.writeHead(200);
res.end(authors);
break;
default:
res.writeHead(404);
res.end(JSON.stringify({error:"Resource not found"}));
}
}İlgili verileri görmek için curl http://localhost:8000/books veya /authors ile test edin. Başka herhangi bir URL ise 404 hatası döner.
Basit dize eşleştirmesiyle ilgili sorun: req.url, alan adından sonraki her şeyi içeren ham bir dizedir—yollar, sorgu dizeleri ve parçalar. Eğer biri /books?id=123 adresini ziyaret ederse, sizin case "/books" eşleşmez çünkü req.url /books?id=123 olup /books değildir. Sorgu parametreleri yönlendirmeyi bozar: /books?sort=name /books ile eşleşmez. URL kodlanmış karakterler yönlendirmeyi bozabilir: /books%20reviews (boşluk %20 olarak kodlanmış) /books reviews ile eşleşmez.
Onun yerine URL API’sini kullanın: URL API’si, yol adını sorgu parametrelerinden otomatik olarak ayırır, URL ile kodlanmış karakterleri çözer ve kenar durumları doğru şekilde yönetir.
const host = req.headers.host || 'localhost:8000';
const url = new URL(req.url, `http://${host}`);
switch (url.pathname) {
case '/books':
// Validate HTTP method - only GET allowed for this endpoint
if (req.method !== 'GET') {
res.writeHead(405, { 'Allow': 'GET' });
return res.end(JSON.stringify({ error: 'Method not allowed' }));
}
res.writeHead(200);
return res.end(books);
case '/authors':
if (req.method !== 'GET') {
res.writeHead(405, { 'Allow': 'GET' });
return res.end(JSON.stringify({ error: 'Method not allowed' }));
}
res.writeHead(200);
return res.end(authors);
default:
res.writeHead(404);
return res.end(JSON.stringify({ error: 'Resource not found' }));
}HTTP metodunu neden kontrol etmeliyiz? Aynı yol farklı HTTP metodlarını destekleyebilir (GET veri almak için, POST oluşturmak için). Bu kontrol yapılmazsa, /books adresine yapılan bir POST isteği yeni bir kitap oluşturmak yerine kitap listesini döndürür, bu da REST API kurallarını ihlal eder. 405 Method Not Allowed durum kodu ve Allow başlığı, istemcilere bu uç nokta için hangi metodların geçerli olduğunu bildirir.
Sorgu dizisi işleme: URL API'si, yol adını (/books) sorgu parametrelerinden (?id=123) otomatik olarak ayırır, bu yüzden /books?id=123 doğru şekilde kitaplar uç noktasına yönlendirilir ve gerekirse url.searchParams.get('id') ile erişmeye devam edebilirsiniz.
Çeşitli senaryoları test edin:
curl http://localhost:8000/books
curl http://localhost:8000/authors
curl http://localhost:8000/books?id=123 # Query string doesn't break routing
curl -X POST http://localhost:8000/books # Returns 405 Method Not Allowed
curl http://localhost:8000/nonexistent # Returns 404Adım 5 — POST İsteklerini İşleme ve İstek Gövdelerini Ayrıştırma
Şimdiye kadar yalnızca GET isteklerini ele aldık. Çoğu API'nin istemcilerden veri alması gerekir ve bu genellikle POST istekleri aracılığıyla gerçekleşir. http modülü, istek gövdelerini manuel olarak ayrıştırmayı gerektirir; bu gövdeler akışlar olarak alınır.
Önemli bilgi: Verilerin URL'de geldiği GET isteklerinin aksine, POST isteklerinin gövdeleri bir akış olarak gelir. Bu, verilerin tek seferde gelmediği, parçalar halinde geldiği anlamına gelir. Tüm parçalar gelmeden JSON'u çözmeye çalışırsanız, eksik veya hatalı veri alırsınız. Önce tüm parçaları toplamalı, ardından çözümlemelisiniz.
İşte çok erken çözümlemeye çalışırsanız ne olur:
// WRONG - Body isn't complete yet
req.on('data', chunk => {
const data = JSON.parse(chunk); // Will fail - chunk is only part of the body
});
// CORRECT - Collect all chunks first
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
const data = JSON.parse(body); // Now body is complete
});JSON yükleri kabul eden POST isteklerini alan bir sunucu oluşturun. Yeni bir dosya oluşturun:
touch post.js
cp routes.js post.jspost.js dosyasını açın:
nano post.jsİşte güvenlik konularını ele alan üretime hazır bir JSON gövde ayrıştırıcı:
const MAX_BODY = 1 * 1024 * 1024; // 1MB limit prevents memory exhaustion
function parseJSONBody(req, res) {
return new Promise((resolve, reject) => {
let body = '';
let received = 0;
// Validate Content-Type before processing
const contentType = req.headers['content-type'] || '';
if (!contentType.startsWith('application/json')) {
res.writeHead(415, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Unsupported Content-Type' }));
return reject(new Error('Unsupported Content-Type'));
}
// Collect data chunks
req.on('data', chunk => {
received += chunk.length;
// Prevent memory exhaustion from huge payloads
if (received > MAX_BODY) {
res.writeHead(413, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Payload too large' }));
req.destroy(); // Stop receiving more data
return reject(new Error('Payload too large'));
}
body += chunk.toString(); // Convert Buffer to string
});
// All chunks received, now parse
req.on('end', () => {
try {
if (!body) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Empty request body' }));
return reject(new Error('Empty request body'));
}
const data = JSON.parse(body);
// Validate it's an object (not a string, number, etc.)
if (typeof data !== 'object' || data === null || Array.isArray(data)) {
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Invalid JSON object' }));
return reject(new Error('Invalid JSON object'));
}
resolve(data);
} catch (parseError) {
// JSON.parse() throws for malformed JSON
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Malformed JSON' }));
console.error('JSON parse error:', parseError.message);
}
});
// Handle network errors
req.on('error', (error) => {
console.error('Request error:', error.message);
res.writeHead(400, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({ error: 'Request error' }));
});
});
}Content-Type neden kontrol edilir? Eğer bir istemci form verisini Content-Type: application/x-www-form-urlencoded ile gönderirse, bunu JSON olarak çözümlemeye çalışmak başarısız olur. 415 Unsupported Media Type durum kodu, istemciye Content-Type bilgisinin kabul edilebilir olmadığını bildirir.
Vücut boyutunu neden sınırlamalıyız? Bir sınır olmadan, kötü niyetli bir istemci gigabaytlarca veri gönderebilir ve sunucunuzun belleğini tüketebilir. 413 Payload Too Large yanıtı ve req.destroy(), sınır aşıldığında bağlantıyı hemen durdurur.
Parçalanmış sonucu neden doğrulamalıyız? JSON.parse('"hello"') başarılı olur ama bir nesne yerine bir string döner. Kodunuz data.property şeklinde çalışmayı bekliyor olabilir, ancak stringlerin özellikleri yoktur. Bu kontrol, gerçekten bir nesne elde etmenizi sağlar.
Bu işlevi POST işleyicinizde kullanın:
const requestListener = async function (req, res) {
if (req.method === 'POST' && req.url === '/books') {
const bookData = await parseJSONBody(req, res);
// Now use bookData safely
// ...
}
};Adım 6 — Statik Dosyaları Sunma
Birçok web uygulaması, resimler, CSS dosyaları, JavaScript dosyaları veya diğer kaynaklar gibi statik varlıkları sunmak zorundadır. http modülü, Express’in express.static özelliği gibi yerleşik bir statik dosya servisi sunmasa da, bunu fs ve path modüllerini kullanarak uygulayabilirsiniz.
Kritik güvenlik sorunu: Eğer doğrulama yapmadan path.join(__dirname, 'public', req.url) kullanırsanız, bir saldırgan dizin geçişi saldırıları ile sunucunuzdaki herhangi bir dosyaya erişebilir. GET /../../../etc/passwd gibi bir istek sistem dosyalarını açığa çıkarabilir. path.resolve() kontrolü, çözümlenen yolun public dizininden asla çıkmamasını garanti eder.
İşte uygun yol doğrulaması yapılmazsa nelerin yanlış gidebileceği:
// VULNERABLE - Allows directory traversal
const filePath = path.join(__dirname, 'public', req.url);
// Request: /../../../etc/passwd
// Resolves to: /etc/passwd (outside public directory!)
// SECURE - Validates path stays within public directory
const safePath = path.resolve(publicDir, '.' + reqPath);
if (!safePath.startsWith(publicDir)) {
res.writeHead(403);
res.end('Forbidden');
return;
}
// Request: /../../../etc/passwd
// safePath: /home/user/app/public/../../../etc/passwd
// Resolved: /etc/passwd
// Check fails because /etc/passwd doesn't start with /home/user/app/publicGüvenli bir statik dosya sunucusu oluşturun:
touch static.jsstatic.js dosyasını açın:
nano static.jsStatik dosyaları verimli ve güvenli bir şekilde sunmak için fs.createReadStream() ile akış sağlayın ve güvenli yol çözümlemesi kullanın:
const http = require("http");
const fs = require("fs");
const path = require("path");
const host = 'localhost';
const port = 8000;
const mimeTypes = {
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.gif': 'image/gif',
'.svg': 'image/svg+xml',
'.ico': 'image/x-icon',
'.pdf': 'application/pdf'
};
const publicDir = path.resolve(__dirname, 'public');
const requestListener = (req, res) => {
// Normalize the request path
let reqPath = req.url === '/' ? '/index.html' : req.url;
// Strip query string - ?id=123 shouldn't affect file path
reqPath = reqPath.split('?')[0];
// Resolve path and check it stays within publicDir
// The '.' prefix prevents absolute paths from being resolved incorrectly
const safePath = path.resolve(publicDir, '.' + reqPath);
// Critical security check: ensure resolved path is within publicDir
if (!safePath.startsWith(publicDir)) {
res.writeHead(403, { 'X-Content-Type-Options': 'nosniff' });
res.end('Forbidden');
return;
}
// Check if file exists and is actually a file (not a directory)
fs.stat(safePath, (err, stat) => {
if (err || !stat.isFile()) {
res.writeHead(404, { 'X-Content-Type-Options': 'nosniff' });
res.end('File not found');
return;
}
// Determine MIME type from file extension
const ext = path.extname(safePath).toLowerCase();
const contentType = mimeTypes[ext] || 'application/octet-stream';
res.setHeader("Content-Type", contentType);
res.setHeader("X-Content-Type-Options", "nosniff"); // Prevents MIME sniffing attacks
res.writeHead(200);
// Stream file instead of loading into memory
// Critical for large files - won't consume all memory
const stream = fs.createReadStream(safePath);
stream.pipe(res);
stream.on('error', () => {
// File might be deleted between stat() and createReadStream()
res.writeHead(500, { 'X-Content-Type-Options': 'nosniff' });
res.end('Server error');
});
});
};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
console.log(`Serving static files from: ${publicDir}`);
});Bir public dizini oluşturun ve bir test dosyası ekleyin:
mkdir public
echo "<h1>Static File Served!</h1>" > public/index.htmlSunucuyu çalıştır:
node static.jsStatik dosyanın servis edildiğini görmek için tarayıcınızda http://localhost:8000 adresini ziyaret edin.
Niçin fs.readFile() yerine akış (streaming) kullanılmalı? 100MB'lık bir video dosyası için, fs.readFile() dosyanın tamamını belleğe yükler ve sonra gönderir. fs.createReadStream() ile veri, dosya boyutu ne olursa olsun minimum bellek kullanarak doğrudan diskten ağa küçük parçalar halinde aktarılır.
Niçin stat.isFile() kontrol edilir? Birisi /images/ gibi bir dizin yolu isteğinde bulunduğunda, fs.stat() başarılı olur ama isFile() false döner. Bu kontrol olmadan, bir dizini aktarmaya çalışırsınız ki bu başarısız olur. Bu kontrol, sadece gerçek dosyaları sunmanızı sağlar.
Yol geçişi korumasını test et:
curl http://localhost:8000/../../../etc/passwd
# Should return 403 Forbidden, not the system fileAdım 7 — Hata Yönetimi ve Kayıt Tutma
Sağlam hata yönetimi, üretim sunucuları için çok önemlidir. http modülü otomatik hata yönetimi sağlamaz, bu yüzden bunu kendiniz uygulamanız gerekir.
Doğru hata yönetimi, asenkron hataları yakalamayı, bunları uygun şekilde kaydetmeyi, kullanıcı dostu hata yanıtları göndermeyi ve işlenmemiş hatalardan kaynaklanan sunucu çökmesini önlemeyi içerir. Hadi sunucumuzu kapsamlı hata yönetimi ve kayıt tutma ile geliştirelim:
const http = require("http");
const fs = require('fs').promises;
const host = 'localhost';
const port = 8000;
// Simple logging utility
const log = {
info: (msg) => console.log(`[INFO] ${new Date().toISOString()} - ${msg}`),
error: (msg, err) => console.error(`[ERROR] ${new Date().toISOString()} - ${msg}`, err),
warn: (msg) => console.warn(`[WARN] ${new Date().toISOString()} - ${msg}`)
};
const requestListener = async function (req, res) {
try {
log.info(`${req.method} ${req.url} - ${req.headers['user-agent'] || 'Unknown'}`);
if (req.url === '/') {
try {
const data = await fs.readFile(__dirname + '/index.html');
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(data);
} catch (error) {
log.error('Failed to read index.html', error);
res.writeHead(500);
res.end('<h1>Internal Server Error</h1><p>Unable to load page.</p>');
}
} else if (req.url === '/error') {
throw new Error('Simulated error for testing');
} else {
res.writeHead(404);
res.end('<h1>Not Found</h1><p>The requested resource was not found.</p>');
}
} catch (error) {
log.error('Unhandled error in request handler', error);
const errorMessage = process.env.NODE_ENV !== 'production'
? error.message
: 'An unexpected error occurred';
res.writeHead(500);
res.end(`<h1>Internal Server Error</h1><p>${errorMessage}</p>`);
}
};
const server = http.createServer(requestListener);
// Handle server-level errors
server.on('error', (error) => {
if (error.code === 'EADDRINUSE') {
log.error(`Port ${port} is already in use`);
} else {
log.error('Server error', error);
}
process.exit(1);
});
// Handle client connection errors
server.on('clientError', (err, socket) => {
log.warn('Client error: ' + err.message);
socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});
server.listen(port, host, () => {
log.info(`Server is running on http://${host}:${port}`);
});Nazik Kapatma (SIGTERM/SIGINT)
PM2 veya Docker gibi işlem yöneticileri altında çalışırken, sunucuyu düzgün bir şekilde kapatmak için sonlandırma sinyallerini yönetin:
const connections = new Set();
server.on('connection', socket => {
connections.add(socket);
socket.on('close', () => connections.delete(socket));
});
const shutdown = (signal) => {
log.warn(`${signal} received: shutting down gracefully...`);
server.close(() => {
log.info('Server closed.');
process.exit(0);
});
setTimeout(() => {
log.warn('Forcing socket close...');
connections.forEach(s => s.destroy());
process.exit(1);
}, 10000).unref();
};
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));Gösterilen temel hata işleme uygulamaları:
- Asenkron işlemler etrafında try-catch blokları
- Sunucu düzeyi hata yöneticileri bağlantı ve port sorunları için
- Bozuk istekler için istemci hata yönetimi
- Zaman damgaları ve önem dereceleri ile Kayıt Tutma
- Ortam farkında hata mesajları (üretimde yığın izlerini açığa çıkarmayın)
- Zarif hata yanıtları ve uygun HTTP durum kodları
En İyi Uygulama: Üretim ortamında, Winston veya Pino gibi yapılandırılmış loglama kütüphanelerini kullanmayı ve kritik hataları Sentry veya Rollbar gibi hata izleme servislerine göndermeyi düşünün. Bu örnek, eğitim amaçlı basit konsol loglaması kullanmaktadır.
Adım 8 — ES Modülleri (ESM) Söz Dizimini Kullanma
Modern Node.js, CommonJS require/module.exports yerine import/export sözdizimini kullanarak ES Modüllerini (ESM) destekler. ESM kullanmak için ya dosyanızı .mjs uzantısıyla adlandırın ya da package.json dosyanızda "type": "module" olarak ayarlayın.
ESM sözdizimi, require yerine import ve module.exports yerine export kullanır. İşte aynı temel sunucunun ES Modül sözdizimini kullanan versiyonu:
server.mjs oluşturun:
touch server.mjs
nano server.mjsimport http from 'http';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
// ES Modules don't have __dirname, so we recreate it
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const host = 'localhost';
const port = 8000;
const requestListener = async (req, res) => {
try {
if (req.url === '/') {
const data = await fs.readFile(path.join(__dirname, 'index.html'));
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
res.end(data);
} else {
res.writeHead(404);
res.end('Not found');
}
} catch (error) {
console.error('Error:', error);
res.writeHead(500);
res.end('Server error');
}
};
const server = http.createServer(requestListener);
server.listen(port, host, () => {
console.log(`Server is running on http://${host}:${port}`);
});Çalıştır:
node server.mjsTemel Farklar: ESM’de __dirname mevcut değildir—bunu import.meta.url üzerinden elde etmeniz gerekir. Ayrıca, fs işlemlerindeki dosya yollarının açıkça çözülmesi gerekebilir. Aksi takdirde, HTTP modülü API’si aynı kalır.
Alternatif olarak, "type": "module" ile package.json oluşturun:
{
"type": "module"
}Sonra ESM sözdizimi ile .js dosyalarını kullanın:
import http from 'http';
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('ESM Server');
});
server.listen(8000);Üretim Hususları
Geliştirmeden üretime geçerken, birkaç önemli husus göz önünde bulundurulmalıdır:
HTTPS Desteği
http modülü yalnızca HTTP hizmeti verir. HTTPS için, benzer şekilde https modülünü kullanın:
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('path/to/private-key.pem'),
cert: fs.readFileSync('path/to/certificate.pem')
};
const server = https.createServer(options, requestListener);Güvenlik: Özel anahtarları veya sertifikaları sürüm kontrolüne asla eklemeyin. Üretim ortamında çevresel değişkenler veya güvenli gizli yönetim hizmetleri kullanın.
Ters Vekil Sunucu Yapılandırması
Üretimde, Node.js sunucuları genellikle Nginx veya Apache gibi ters vekil sunucuların arkasında çalışır. Proxy, SSL sona erdirmeyi, statik dosyaları ve yük dengelemesini yönetir. Node.js sunucunuz şunları yapmalıdır:
- Yalnızca
localhostveya127.0.0.1üzerinde dinle - İstemci IP adresleri için
X-Forwarded-Forbaşlıklarını okuyun - Sadece kendi proxy sunucunuzdan gelen proxy başlıklarına güvenin
Kurulum detayları için Ubuntu üzerinde Nginx'i Ters Vekil Sunucu olarak nasıl yapılandıracağınızı gösteren rehberimize göz atın.
Süreç Yönetimi
Üretimde, şunları yönetmek için PM2 gibi işlem yöneticilerini kullanın:
- Çökme durumunda otomatik yeniden başlatmalar
- Sıfır kesintiyle dağıtımlar
- Kayıt yönetimi
- Birden fazla CPU çekirdeğini kullanmak için küme modu
Ubuntu üzerinde bir Node.js uygulamasını üretime hazırlama rehberimizde daha fazlasını öğrenin.
Performans Optimizasyonu
Dosya önbellekleme: Statik dosyaları her istekte diskte okumak yerine başlangıçta yükleyin (Adım 3'te gösterildiği gibi). Bu, G/Ç yükünü önemli ölçüde azaltır.
Büyük dosyaları akıtın: 1MB’dan büyük dosyalar için tüm dosyayı belleğe yüklemek yerine
fs.createReadStream()kullanın ve yanıt akışına yönlendirin.Bağlantı havuzu: Veritabanı bağlantıları için, her istekte yeni bağlantılar oluşturma yükünü önlemek amacıyla bağlantı havuzu kullanın.
Sıkıştırma: Bant genişliğini azaltmak için metin yanıtlarında gzip sıkıştırması için
zlibmodülünü kullanın:const zlib = require('zlib'); const gzip = zlib.createGzip(); res.setHeader('Content-Encoding', 'gzip'); gzip.pipe(res); gzip.write(data); gzip.end();Cluster modu: CPU yoğun işlemler için verimi maksimize etmek amacıyla, CPU çekirdekleri sayısına eşit sayıda işçi süreci oluşturmak için Node.js
clustermodülünü kullanın.İstek zaman aşımı: Yavaş istemcilerin kaynakları tüketmesini önlemek için makul zaman aşımı değerleri belirleyin:
server.setTimeout(30000); // 30 secondsBellek izlemesi: Bellek kullanımını takip ederek sızıntıları erken tespit edin. Canlı ortamda, yığın boyutunu izleyin ve bellek sınırsız artarsa düzgün yeniden başlatmalar uygulayın.
Önbellekleme ve Sıkıştırma En İyi Uygulamaları
Performansı daha da artırmak ve yanıt sürelerini azaltmak için uygun yerlerde önbellekleme ve sıkıştırma uygulayın.
Statik dosyalar için önbellekleme etkinleştirin:
Statik dosya yanıtlarınızaCache-ControlveETagbaşlıkları ekleyin. Örneğin:const stat = fs.statSync(safePath); res.setHeader('Cache-Control', 'public, max-age=86400'); // cache for 1 day res.setHeader('ETag', `"${stat.size}-${stat.mtime.getTime()}"`);Tarayıcılar ve CDN'ler, ETag veya önbellek süresi dolana kadar önbelleğe alınmış kopyaları tekrar kullanacaktır.
Gzip veya Brotli sıkıştırmasını kullanın:
HTML, CSS, JS ve JSON gibi metin tabanlı yanıtlar oldukça iyi sıkıştırılabilir. Sıkıştırmayı Node’unzlibmodülü ile uygulayabilirsiniz:const zlib = require('zlib'); const stream = fs.createReadStream(safePath); res.setHeader('Content-Encoding', 'gzip'); stream.pipe(zlib.createGzip()).pipe(res);Bu, bant genişliği kullanımını azaltır ve daha yavaş bağlantıya sahip müşteriler için teslimatı hızlandırır.
Dinamik uç noktaların aşırı önbelleğe alınmasından kaçının:
API'ler veya sık güncellenen veriler için daha kısa önbellek süreleri belirleyin (ör.max-age=60) veya gerektiğindeno-storekullanın.
Bu optimizasyonlar, statik dosya akışı ve düzgün kapanmayı tamamlayarak Node.js sunucunuzun yüksek performans sunmasına yardımcı olurken güvenli ve öngörülebilir kalmasını sağlar.
Minimal API'ler, özel yönlendirme veya HTTP dahili yapısını öğrenmek için Node.js http modülünü kullanın.
Ara katman yazılım, şablonlama veya hızlı prototipleme gerektiğinde Express/Fastify kullanın.
Yaygın Sorunları Giderme
Port Zaten Kullanımda (EADDRINUSE)
Hata: Hata: dinleme EADDRINUSE: adres zaten kullanımda :::8000
Çözüm:
- Portu kullanan süreci bulun:
lsof -i :8000(macOS/Linux) veyanetstat -ano | findstr :8000(Windows) - Süreci sonlandırın veya başka bir port kullanın
- Bunu sorunsuz bir şekilde yakalamak için hata yönetimini (Adım 7'de gösterildiği gibi) ekleyin
İstek Gövdesi Alınmadı
Problem: POST isteği gövdesi boş veya tanımsız
Çözüm: İstek gövdeleri akışlardır—parçaları toplamanız gerekir. Her zaman 5. Adımda gösterilen deseni kullanın:
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
// Now parse body
});Tarayıcıda CORS Hataları
Sorun: Tarayıcı CORS politikası nedeniyle istekleri engelliyor
Çözüm: Yanıtınıza CORS başlıklarını ekleyin:
res.setHeader("Access-Control-Allow-Origin", "*"); // Or specific domain
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type");Güvenlik: Access-Control-Allow-Origin için "*" kullanmak tüm kaynaklara izin verir. Üretim ortamında frontend alanınızı açıkça belirtin.
Statik Dosyalar 404 Döndürüyor
Sorun: Statik dosyalar var olmasına rağmen bulunamıyor
Çözüm:
- Dosya yolu çözümlemesini kontrol et
__dirname'in doğru dizini gösterdiğini doğrulayın- Dosya izinlerinin okuma işlemi için uygun olduğundan emin olun
- Yol temizlemenin geçerli karakterleri silmediğini doğrulayın
Sıkça Sorulan Sorular
Node.js'te HTTP modülü nedir?
http modülü, HTTP sunucuları ve istemcileri oluşturmak için Node.js’in yerleşik modülüdür. Dış bağımlılıklara gerek duymadan HTTP isteklerini ve yanıtlarını işlemek için düşük seviyeli API’ler sunar. Node.js çekirdeğinin bir parçasıdır, bu yüzden her Node.js kurulumunda mevcuttur.
Node.js'te basit bir web sunucusunu nasıl oluşturabilirim?
http.createServer() kullanarak bir istek dinleyici fonksiyonuyla bir sunucu oluşturun:
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200);
res.end('Hello World');
});
server.listen(8000);Bu, 8000 numaralı portta dinleyen ve tüm isteklere “Merhaba Dünya” ile yanıt veren bir sunucu oluşturur.
HTTP modülü ile Express.js arasındaki fark nedir?
http modülü, düşük seviyeli HTTP işlemleri sağlayan Node.js'in temel bir işlevselliğidir. Express.js, yönlendirme, ara katman yazılım (middleware), şablon oluşturma ve diğer kolaylıkları ekleyen http üzerine kurulmuş bir çerçevedir. http modülü size tam kontrol sağlar ancak daha fazla manuel kurulum gerektirir, oysa Express.js ek bağımlılıklar pahasına soyutlamalar sunar.
http modülünü kullanın:
- Hafif API'ler veya mikro servisler oluşturmak
- HTTP temellerini öğrenmek
- Hiç bağımlılık istemiyorsun
- İstek/yanıt işleme üzerinde tam kontrol
Express.js kullanın:
- Birçok rotaya sahip karmaşık uygulamalar geliştirmek
- Ara katman yazılım ekosistemine ihtiyacınız var
- Hızlı prototipleme bir önceliktir
- Topluluk standartlarına uygun desenler istiyorsunuz
Express.js ve web geliştirmeyi nasıl kolaylaştırdığı hakkında kapsamlı bir giriş için, Node.js Express Temelleri rehberimize bakın.
Node.js kullanarak Express olmadan REST API oluşturabilir miyim?
Evet, kesinlikle. http modülü REST API'leri oluşturmak için yeterlidir. Şunları manuel olarak halletmeniz gerekecek:
- Yol ayrıştırma (
req.urlvereq.methodkullanarak) - İstek gövdesi ayrıştırması (akış parçacıklarının toplanması)
- Yanıt biçimlendirme
- Hata yönetimi
Basit API'ler için bu, çerçeve bağımlılıkları eklemeye göre genellikle daha tercih edilir. Yönlendirme ve POST isteği işleme örnekleri için bu öğreticideki 4. ve 5. adımlara bakın.
Node.js HTTP modülünü kullanarak farklı yönlendirmeleri nasıl yönetebilirim?
req.url'i ayrıştırın ve koşullu ifadeler veya switch deyimi kullanın:
const requestListener = (req, res) => {
switch (req.url) {
case '/':
res.writeHead(200);
res.end('Home page');
break;
case '/about':
res.writeHead(200);
res.end('About page');
break;
default:
res.writeHead(404);
res.end('Not found');
}
};Daha karmaşık yönlendirmeler için, URL yollarını ve sorgu dizelerini url modülünü kullanarak ayrıştırabilir ve farklı HTTP yöntemlerini işleyebilirsiniz.
HTTP modülü ile HTML veya JSON yanıtlarını nasıl gönderirim?
Content-Type başlığını uygun şekilde ayarlayın ve veriyi gönderin:
HTML:
res.setHeader('Content-Type', 'text/html');
res.writeHead(200);
res.end('<h1>Hello</h1>');JSON:
res.setHeader('Content-Type', 'application/json');
res.writeHead(200);
res.end(JSON.stringify({ message: 'Hello' }));Content-Type başlığı, istemciye yanıt verilerini nasıl yorumlayacağını söyler.
Yerel bir Node.js sunucusu için hangi portu kullanmalıyım?
Yaygın geliştirme portları şunlardır:
- 8000 veya 8080: Genel HTTP geliştirme
- 3000: Express gibi framework'lerle popüler
- 5000: Ayrıca yaygın
1024 üzerindeki herhangi bir bağlantı noktası root olmayan kullanıcılar için uygundur. 1024 altındaki bağlantı noktaları yönetici ayrıcalıkları gerektirir. Kullanımdan önce lsof -i :PORT (macOS/Linux) ile kullanılabilirliği kontrol edin.
Node.js HTTP sunucusunda 404 hatalarını nasıl yönetebilirim?
Yönlendirme mantığınıza varsayılan bir durum ekleyin:
const requestListener = (req, res) => {
if (req.url === '/') {
res.writeHead(200);
res.end('Home');
} else {
res.writeHead(404);
res.end('Not Found');
}
};HTTP standartlarına uymak için var olmayan rotalar için her zaman durum kodunu 404 olarak ayarlayın.
Statik dosyaları Express olmadan nasıl sunabilirim?
fs modülünü dosyaları okumak için ve güvenli yol çözümlemesi için path modülünü kullanın:
const fs = require('fs').promises;
const path = require('path');
const requestListener = async (req, res) => {
const filePath = path.join(__dirname, 'public', req.url);
try {
const data = await fs.readFile(filePath);
res.writeHead(200);
res.end(data);
} catch (error) {
res.writeHead(404);
res.end('File not found');
}
};Dizin geçişi saldırılarını önlemek için yolları her zaman doğrulayın (güvenli bir uygulama için Adım 6’ya bakın).
Node.js'te HTTP modülü ile ES Modüllerini (import) kullanabilir miyim?
Evet, Node.js ES Modüllerini destekler. Şunu kullanabilirsiniz:
.mjsuzantılı dosya:
import http from 'http';
// ... rest of codepackage.jsondosyasında"type": "module"ayarlayın, ardından.jsdosyalarını kullanın:
{
"type": "module"
}ES Modüllerinin __dirname olmadığına dikkat edin—bunu, Adım 8'de gösterildiği gibi import.meta.url'den türetin.
HTTP modülü ile POST istek gövdelerini nasıl çözümlerim?
POST gövdeleri akışlar olarak gelir. Parçaları toplayın, sonra ayrıştırın:
const parseBody = (req) => {
return new Promise((resolve, reject) => {
let body = '';
req.on('data', chunk => body += chunk.toString());
req.on('end', () => {
try {
resolve(JSON.parse(body)); // For JSON
} catch (error) {
reject(error);
}
});
req.on('error', reject);
});
};Bu desen, JSON, form verisi veya herhangi bir metin tabanlı içerik türü için çalışır.
HTTP modülü üretim kullanımına uygun mu?
Evet, http modülü üretime hazırdır ve birçok Node.js uygulaması tarafından kullanılmaktadır. Ancak, şunları göz önünde bulundurun:
- İşlem yönetimi: Otomatik yeniden başlatmalar için PM2 veya benzerini kullanın
- Ters proxy: SSL ve yük dengeleme için Nginx/Apache arkasında çalıştırın
- Hata yönetimi: Kapsamlı hata yönetimi ve günlük kaydı uygulayın
- Güvenlik: Girdileri doğrulayın, yolları temizleyin, istekler için boyut sınırları belirleyin
- Performans: Statik dosyaları önbelleğe al, CPU’ya bağlı işlemler için kümeleme kullan
Birçok üretim uygulaması, özellikle mikro servisler ve hafif API'ler, http modülünü doğrudan kullanır. Kapsamlı üretim dağıtım kılavuzu için Ubuntu 22.04 üzerinde Node.js Uygulamasının Üretime Hazırlanması başlığına bakın.
Sonuç
Bu eğitimde, baştan sona üretim için uygun Node.js HTTP sunucuları kurdunuz. Temel bir metin yanıtıyla başladınız, ardından birden fazla içerik türü (JSON, CSV ve HTML) sunmaya geçtiniz. Farklı URL'leri ele almak için yönlendirme uyguladınız, POST istek gövdelerini çözdünüz, statik dosyaları güvenli bir şekilde sundunuz ve kapsamlı hata yönetimi ve günlükleme eklediniz. Ayrıca, CommonJS ve ES Modül sözdizimini keşfettiniz ve üretim dağıtımıyla ilgili hususları incelediniz.
Artık çerçeve bağımlılıkları olmadan çeşitli gereksinimleri karşılayan web sunucuları oluşturabilecek bilgiye sahipsiniz. Bu bilgi ile şunları yapabilirsiniz:
- Mikroservisler için hafif REST API'leri oluşturun
- Doğru yönlendirme ile çok sayfalı web siteleri sunun
- Dosya yüklemeleri ve indirmelerini yönet
- Özel ara katman mantığını uygula
- Çerçevelerin değer kattığı ve gereksiz karmaşıklık yarattığı durumlar hakkında bilinçli kararlar verin
Sonraki adımlar:
- Node.js temellerine daha derinlemesine dalın: Kapsamlı bir inceleme için Node.js ile Kodlama Nasıl Yapılır serisine geri dönün
- Resmi belgelere göz atın: Node.js HTTP modülü belgeleri ayrıntılı API referansları ve ileri düzey kullanım örneklerini içerir
- Framework özelliklerine ihtiyacınız olduğunda: Orta katman ve şablonlama desteği ile hızlı geliştirme için Express.js temellerini göz önünde bulundurun
Bu HTTP temellerini anlamak, sizi daha yetenekli bir Node.js geliştiricisi yapar. İster frameworkler kullanmayı seçin, ister doğrudan http modülü ile çalışın, artık tüm Node.js web uygulamalarını çalıştıran temel mekanizmaları anlıyorsunuz. Bu bilgi, sorunları çözmenize, performansı optimize etmenize ve etkili bir şekilde ölçeklenen sistemler tasarlamanıza yardımcı olur.
Linux-Console.net Topluluğu ile öğrenme sürecine katıldığınız için teşekkürler.

