Adım Adım Nodejs yazı dizimizin bir önceki bölümünde expressjs frameworküne giriş yapmış, ilk uygulamamızı yazmıştık. Devamında routing yani yönlendirme işlemlerini ele aldık. Son olarakta statik dosya yönetimini anlatıp sonlandırdık. Bu adımda expressjs içerisinde bulunan middleware kavramından ve express application generator’den bahsedeceğim.
Middleware
Bu bölümde ise middleware kavramıyla başlayacağız. Web projeleri doğası gereği request-response yapısına sahiptir. Kullanıcı yazmış olduğumuz uygulamaya bir istekte bulunur(request). Uygulama bu isteği alır belli aşamalardan geçirerek istek yapan kullanıcıya bir cevap oluşturur ve yollar(response). Burada bahsettiğimiz aşamalar kullanmış olduğumuz framework’e göre değişir. Bizim nodejs ile kullandığımız expressjs de belirli aşamalara sahiptir. Gelen istekler expressjs’nin yaşam döngüsü içerisindeki her bir aşamadan geçerek cevap oluşturulur. İşte bu her bir aşamaya expressjs’de middleware denir.
Middleware aslında bir fonksiyondur. Fonksiyon imzası şu şekildedir: function(req, res, next) { … } .Buradaki req (request) ile bize gönderilen istek hakkındaki bilgilere ulaşabiliriz, res (response) ile verilecek olan cevaba müdahale edebiliriz, next ile de bir sonraki middleware fonksiyonunu çalıştırabiliriz. Şimdi basit bir middleware fonksiyonu yazalım. Bu middleware fonksiyonu her gelen istekte konsol üzerine log yazacak. Kodlarımız şu şekilde:
var express=require('express'); var app=express(); var logger= function (req, res, next) { var tarih = new Date(); var formatliTarih = tarih.getDate() + "." + (tarih.getMonth() + 1) + "." + tarih.getFullYear(); formatliTarih += " " + tarih.getHours() + ":" + tarih.getMinutes(); console.log(formatliTarih + ':Yeni istek') next() }; app.use(logger); app.get('/',function (req,res) { res.send('Merhaba Dünya!'); }); app.listen(3000,function () { console.log('Uygulama 3000 portunda çalışmakta.') });
Yukardaki kodları incelediğimizde daha önceki yazılarımda anlattığım web server kodları ile hemen hemen aynı. Fark 4. satırda başlıyor. logger adında bir fonksiyon oluşturuyoruz. Bu fonksiyon 3 tane parametre alıyor: req, res ve next. Yani middleware fonksiyon formatına uygun. Bu fonksiyon içerisinde 8. adımda bahsetmiş olduğumuz tarih formatını oluşturuyoruz. Diğer adımda da konsol üzerine bu tarihi ve “Yeni istek” yazısını yazdırıyoruz. Fonksiyonun son satırında da parametre olarak gelen next() fonksiyonunu çalıştırıyoruz. 12. satırdada app.use(logger); ile bu fonksiyonu uygulamamıza bir middleware olarak kullanmasını söylüyoruz. Daha sonraki kodları 9. Adımda anlatmıştık. Uygulamamızı çalıştırdığımızda logger middleware gelen her istek için konsol ekranına tarihi ve yeni bir istek geldiğini belirten yazısını yazacak. En basit hali ile bir middleware fonksiyonu yazmış olduk.
Bir sonraki örneğimizde bu middleware yapısını modülleştirip dışardan yazılmış middleware fonksiyonlarını nasıl kullanacağımızı göreceğiz. logger-middleware.js dosyamızdaki kodlarımız şu şekilde:
module.exports = function (ayarlar) { return function (req, res, next) { if (ayarlar.tarih) { var tarih = new Date(); var formatliTarih = tarih.getDate() + "." + (tarih.getMonth() + 1) + "." + tarih.getFullYear(); formatliTarih += " " + tarih.getHours() + ":" + tarih.getMinutes(); console.log(formatliTarih + ':Yeni istek') } else { console.log('Yeni istek') } next() } }
Burada basit bir modül yazdık. Aslında önceki örneğimizdeki logger middleware fonksiyonunu modüle taşıdık. Ek olarak ayarlar ekledik. Fonksiyon içerisinede bir if bloğu ile eğer ayarlar içerisindeki tarih değişkeni true ise konsola tarihle beraber “Yeni istek” yazdır değilse sadece “Yeni istek” yazdır dedik. Birde bu logger modülümüzün kullanımına bakalım. program.js dosyamızın son hali şu şekilde:
var express = require('express'); var logger = require('./logger-middleware') var app = express(); app.use(logger({ tarih: true })); app.get('/', function (req, res) { res.send('Merhaba Dünya!'); }); app.listen(3000, function () { console.log('Uygulama 3000 portunda çalışmakta.') });
Burasıda önceki örnek ile neredeyse aynı. En üstte logger-modüle modülümüzü logger değişkenine atadık. Bu değişkenide 6. satırda kullandık. Tek fark içerisine bir nesne alıyor olması. Bu nesnede bizim ayarlar değişkenine karşılık gelmektedir. { tarih:true } kısmını ayrı bir değişkene atayıp sonra o değişkeni buraya verebilirdik. Ama javascript ekosistemindeki genel kullanım bu şekildedir. program.js dosyasını çalıştırdığımızda önceki örnekle aynı çıktıyı elde ederiz. Eğer ayarlardaki tarih değikenine false değerini atarsak farklı bir sonuç görürüz.
Kendi middleware fonksiyonlarımızı yazdık. Bunları modüllere de ayırdık. Peki herşeyi bu şekilde biz mi yazacağız? Tabiki hayır. Expressjs için yazılmış tonlarca middleware bulabilirsiniz. Hatta expressjs içerinde bile birsürü hazır middleware bulunmaktadır. Hatta ve hatta uygulamamızda yazdığımız app.get(…) app.post(…) methodlarının içerisine yazdıklarımızda birer middleware.
Expressjs middleware türlerini 5e ayırmış
- Application-level middleware
- Router-level middleware
- Error-handling middleware
- Built-in middleware
- Third-party middleware
Şimdi bunlara basit birer örnek vererek açıklayalım. Kodlarımız şu şekilde:
var express = require('express'); var app = express(); var router = express.Router(); var cookieParser = require('cookie-parser'); // Application-level middleware app.use(function (req, res, next) { console.log(Date.now() + ': Yeni istek, Method:' + req.method + ', URL:' + req.originalUrl) next(); }); app.get('/', function (req, res, next) { res.send('Merhaba Dünya!'); next(); }); app.get('/', function (req, res, next) { res.send('Ana Sayfa'); }); // Router-level middleware router.use(function (req, res, next) { console.log('Yeni istek(Router-level middleware):', Date.now()); next(); }) app.use('/', router) // Error-handling middleware app.use(function (err, req, res, next) { console.error("Hata:" + err.stack); res.status(500).send('Bir hata oluştu!'); }); // Built-in middleware app.use(express.static('public')); // Third-party middleware app.use(cookieParser()); app.listen(3000, function () { console.log('Uygulama 3000 portunda çalışmakta.'); });
Örneğimizi incelediğimizde en üstte modüllerimiz yüklüyoruz. Daha sonrasında middleware kullanımlarını görüyoruz. Daha önce kullandığımız app.use(…), app.METHOD(…) (METHOD dan kastımız GET,PUT,POST,DELETE… gibi http metotları) kullanımları aslında bir Application-level middleware. Burada farklı olarak sadece dikkat etmenizi istediğim aynı route tanımına sahip app.get(‘/’, … ) fonksiyonlarının nasıl çalıştığı. Dikkat ederseniz ilk app.get(‘/’, … ) tanımından sonra next() fonksiyonu çalışıyor. Bu sayede ilk middleware kendi içerisindeki işini tamamladıktan sonra diğer middleware’a geçmek için next() fonksiyonunu çalıştırıyor. Bir sonraki app.get(‘/’, … ) middleware’ın route tanımıda gelen isteğe uyduğu için oda çalışıyor. İkinci app.get(‘/’, … ) tanımı içerisinde next() çalıştırılmadığı için 3. bir app.get(‘/’, … ) tanımı olsaydı çalışmazdı.
22. satıra bakacak olursak daha önce kullanmadığımız expressjs içerisindeki Route-level middleware kullanımı görmekteyiz. Bunu kullanmak için öncelikle en üstte var router = express.Router() tanımı yaptığımıza dikkat edin. Aslında kullanım olarak daha önceki app.METHOD( … ) kullanımından pek bi farkı yok. 22. satırda başlayan route-level middleware kullanımı öncekilerle birebir aynı. Asıl dikkat edilmesi gereken yer 27. satırda yapılan app.use(‘/’, router) tanımı. Bu tanım ile expressjs route tanımı içerisinde yapmış olduğumuz değişiklikleri expressjs sistemine tanıtmış oluyoruz.
Bir diğer middleware çeşidi de Error-handling middleware. Error-handling middleware sistem üzerinde herhangi bir hata gerçekleştiğinde çalışır. 30. satırda tanımladığımız Error-handling middleware kullanımında dikkat etmemiz gereken yer fonksiyon imzası. Burada kullanılan fonksiyon 4 parametre almaktadır: app.use(function(err, req, res, next ){ … }); . Buradaki err parametresi ile meydana gelen hata hakkında ekstra bilgiler elde edip hata türüne göre işlem yapabiliriz.
36. satırda Built-in middleware örneği bulunmakta. Aslında bu tanım daha kullandığımız statik dosya tanımlama adımı. public klasöründeki varolan dosyaları dış kullanıma açmaya yarıyor.
Son middleware türümüzde Third-party middleware’lar. Örneğimiz 39. satırda. Tabi bunları kullanmadan önce en üstte yaptığımız gibi var cookieParser = require(‘cookie-parser’) şeklinde modüllerini yüklememiz gerekiyor. Aslında bunlar bizimde yukarıda kendimiz için yazdığımız middleware gibi expressjs topluluğunun genel kullanım için yazdığı middleware’lar. Cookie-parser da bunlardan birtanesi. Bunun gibi daha bir çok third-party middleware listesine buradan ulaşabilirsiniz. Tabi hepsinin ne işe yaradığını ve nasıl kullanıldığını herbirinin kendi dökümantasyonunda bulabilirsiniz.
Express application generator
Express application generator aslında küçük bir npm aracı. Bu araç ile expressjs için önerilen dosya sistemini otomatik oluşturabilir, varsayılan paketleri indirebilir, sunduğu seçenekler ile view engine, css engine gibi sık kullanılan yardımcı araçlardan istediğimizi seçebiliriz.
Öncelikle yüklememiz gerekiyor. Yüklemek için şu komutu çalıştırmamız yeterli:
npm install express-generator -g
Kulllanımı hakkında bilgi almak için terminalde şu komutu vermemiz yeterli:
express --help
Bu komutun çıktısı şu şekilde:
> express --help Usage: express [options] [dir] Options: --version output the version number -e, --ejs add ejs engine support --pug add pug engine support --hbs add handlebars engine support -H, --hogan add hogan.js engine support -v, --view <engine> add view <engine> support (dust|ejs|hbs|hjs|jade|pug|twig|vash) (defaults to jade) -c, --css <engine> add stylesheet <engine> support (less|stylus|compass|sass) (defaults to plain css) --git add .gitignore -f, --force force on non-empty directory -h, --help output usage information
Gördüğünüz gibi kullanımı oldukça kolay. Yeni bir proje oluşturmak için terminalde projeyi oluşturmak istediğimiz klasöre gidiyoruz. Daha sonra express –view=pug uygulamaAdi komutunu vermemiz yeterli. Bu komutun çıktısı da şu şekilde:
> express --view=pug uygulamaAdi create : uygulamaAdi create : uygulamaAdi/package.json create : uygulamaAdi/app.js create : uygulamaAdi/public create : uygulamaAdi/routes create : uygulamaAdi/routes/index.js create : uygulamaAdi/routes/users.js create : uygulamaAdi/views create : uygulamaAdi/views/index.pug create : uygulamaAdi/views/layout.pug create : uygulamaAdi/views/error.pug create : uygulamaAdi/bin create : uygulamaAdi/bin/www create : uygulamaAdi/public/javascripts create : uygulamaAdi/public/images create : uygulamaAdi/public/stylesheets create : uygulamaAdi/public/stylesheets/style.css install dependencies: > cd uygulamaAdi && npm install run the app: > SET DEBUG=uygulamaadi:* & npm start
Yukardaki komuta –view=pug parametresini ekleyerek view engine olarak pug kullanmasını istediğimizi belirttik. Daha sonrasında eklediğimiz uygulamaAdi ise tahmin edebileceğiniz gibi oluşturmuş olduğumuz uygulamanın adı(Aynı zamanda uygulamanın klasör adı). Komutu çalıştırdığımızda bize hangi klasörleri oluşturduğunu listeliyor. Hemen altında cd uygulamaAdi && npm install komutu ile bağımlı olduğu paketleri yüklememiz gerektiğini belirtiyor. Sonrasında ise SET DEBUG=uygulamaadi:* & npm start komutu ile uygulamamızı çalıştırabileceğimizi belirtiyor. Tabi buradaki dosyalar ve paketler varsayılan, klasör yapısı ise tavsiye edilen klasör yapısıdır. Bunları istediğiniz gibi değiştirebilir ve düzenleyebilirsiniz. Zaten yukarıdaki help içeriğini incelediğimizde view engine, css engine, git desteği, handlebars desteği, ejs, hogan.js gibi ekstra paketler/özellikler ekleyebileğimizi görebiliyoruz. Projenize bunları katarak daha zengin ve kullanımı kolay bir geliştirme ortamı oluşturabilirsiniz. Tabi bunlar dışında julpjs gibi kendinizde dışardan eklemeler yapabilirsiniz. Bunlar tamamen size kalmış.
Makalede kullanılan kodlara buradan ulaşabilirsiniz.