NodeJS Adım 10 – Express Framework 2

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.')
});

express expressjs middleware

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.')
});

express expressjs middleware

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

Temel Terminal Komutları

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.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

 

NodeJS Adım 9 – Express Framework

Adım Adım Nodejs yazı dizimizin bir önceki bölümünde web server oluşturmayı ve kendi web sitelerimizi nasıl yapabileceğimizi gördük.

Peki bu işin daha profesyonel olan bir yolu yok mu? Daha kolay bir şekilde web sitesi yapabileceğimiz, bir çok angarya işten bizi kurtaracak bir yol yok mu? Tabiki var. Nodejs için yazılmış Express Web Framework’u tamda bu işe yarıyor. npm üzerinden paket olarak yükleyebileceğimiz Express sayesinde Url parse işlemlerini(routing) daha kolay bir şekilde yapabilir, statik dosya yönetimi işlerimizi daha kolay bir şekilde yapabiliriz. Kısaca nodejs ile web sitesi/uygulaması yapmak için gerekli tüm alt yapıyı Expressjs bize sağlamaktadır. Express’i yüklemek için konsola npm install express –save  yazmamız yeterli. Express’te diğer paketler gibi yüklenecektir. Küçük bir örnek yapalım.

var express=require('express');
var app=express();

app.get('/',function (req,res) {
    res.send('Merhaba Dünya!');
});

app.listen(3000,function () {
    console.log('Uygulama 3000 portunda çalışmakta.')
});

9-1-express

Yukarıdaki örneğimizde basit bir web uygulaması yaptık. Kodda neler olduğuna takılmayın. Aşağıda ayrıntıları anlatacağım.

Yönlendirme ( Routing )

Routing yani Yönlendirme gelen Url’e göre nasıl bir işlem yapılması gerektiğini, nereye yönlendirilmesi gerektiğini belirttiğimiz yapıdır. Express Framework’de bunu yapması çok kolaydır. Express içerisindeki routing yapısı Http methotlarıyla çalışır. Genel yapı app.METHOT(URL_YOLU, CALLBACK)  şeklindedir.  Metot kısmında Http protokolünün GET, POST, PUT, DELETE … gibi metotlarından biri kullanılır.

“Bildiğiniz gibi web siteleri/uygulamaları HTTP protokolü üzerinde çalışır. Siz bir tarayıcıya site adresini yazdığınızda Sunucuya HTTP protokolü üzerinden HTTP’nin herhangi bir metodu ile beraber gider. Bu sayede sunucu üzerinde url ve http metoduna göre işlem yapılabilir. Varsayılan HTTP Metodu GET metodudur. Siz tarayıcı üzerinden bir adrese girmeye çalıştığınızda tarayıcı varsayılan olarak GET metodunu kullanır.”

URL_YOLU kısmına URL paterni yazılır. Gelen url bu paterne uyuyorsa CALLBACK fonksiyonu çalıştırılır. Callback fonksiyonu iki değişken alır. Birincisi request yani istekler hakkında bilgi aldığımız, diğeri response yani dönüşleri yönettiğimiz değişken. (Değişken adlarını istediğiniz gibi değiştirebilirsiniz.) Routing ile alakalı bir örnek yapalım.

var express=require('express');
var app=express();

app.get('/',function (req,res) {
    res.send('Ana Sayfa!');
});

app.post('/',function (req,res) {
    res.send('Ana Sayfa Post!');
});

app.get('/Sayfa1',function (req,res) {
    res.send('Sayfa 1!');
});

app.post('/SayfaPost',function (req,res) {
    res.send('Sayfa Post!');
});

app.put('/SayfaPut',function (req,res) {
    res.send('Sayfa Put!');
});

app.delete('/SayfaDelete',function (req,res) {
    res.send('Sayfa Delete!');
});

app.listen(3000,function () {
    console.log('Uygulama 3000 portunda çalışmakta.')
});

9-2-express-routing

(Gif’te kullandığım uygulamanın adı Postman. Postman ile web sitelerini/uygulamalarını Url ve Http metoduna göre simule edebilirsiniz. Buradan indirebilirsiniz.)

Kodlarımıza bakacak olursak ilk olarak yüklemiş olduğumuz express paketini express değişkenine attık. Daha sonra app isminde bir express uygulaması oluşturduk. Bir sonraki işlemde bir route oluşturuyoruz. Bu route üzerinde url olarak ‘/’ girildiğinde yani boş bırakıldığında ne olması gerektiğini belirttik. Bunun için oluşturmuş olduğumuz app uygulamasının get fonksiyonunu kullandık. get fonksiyonunun callback fonksiyonu iki değişken alıyor: req,res. Birisi request yani isteği yönettiğimiz req diğeri response yani cevabı yönettiğimiz res değişkeni. Callback fonksiyonu içerisinde res değişkeninin send fonksiyonu ile kullanıcıya bilgi gönderebiliyoruz. Burada kullanıcıya Ana Sayfa! yazısını gönderdik. Diğer route işleminde yine url olarak ‘/’ girildiğinde ama Http Post metodu kullanıldığında ne olması gerektiğine bakıyoruz. Diğer route işlemlerinde farklı Http metodları ve farklı Urller üzerinde neler yapılması gerektiğini belirtiyoruz. Mantık basit. Http metodunu seç, Url belirle, callback yaz. Expressdeki route mantığı bu kadar. Yukarıdaki Gif üzerinde Urllere yanlış Http metot ile bağlanırsanız nasıl tepki verdiğini görebilirsiniz. Örnek kodumuzun son bölümünde de app express uygulamasını 3000 portundan çalışmasını sağlıyoruz.

Route ile alakalı başka bir örnek yapalım. Kodlarımız şöyle:

var express=require('express');
var app=express();

app.get('/',function (req,res) {
    res.send('Ana Sayfa!');
});

app.all('/sayfahepsi',function (req,res) {
    res.send('Sayfa Hepsi!');
});

app.get('/ab?cd',function (req,res) {
    res.send('Sayfa ab?cd!');
});

app.get('/de+fg',function (req,res) {
    res.send('Sayfa de+fg!');
});

app.get('/hi*jk',function (req,res) {
    res.send('Sayfa hi*jk!');
});

app.get('/sayfa/:degisken1/parametre/:degisken2', function(req, res) {
    res.send(req.params);
});

app.listen(3000,function () {
    console.log('Uygulama 3000 portunda çalışmakta.')
});

9-3-express-route

Örneğimize bakacak olursak aslında bir önceki örnek ile neredeyse aynı. Başlangıçta yine bir express uygulaması oluşturduk. Sonrasında route’lar tanımladık. Sonrasında uygulamayı 3000 portu üzerinden açtık. Aradaki fark route tanımlama işlemlerinde. İlk route tanımı ilk örnekteki ile aynı. İkinci route tanımına bakacak olursak, farklı olarak app.all(…) fonksiyonu kullanıldı. Bu fonksiyon URL sağlandığı sürece bu route tanımının bütün HTTP metotlarıyla çalışacağını gösteriyor. Üçüncü route tanımına bakalım. Burada da farklı olarak URL kısmında “/ab?cd” ifadesi kullanıldı. Buradaki ? işareti kendinden önceki gelen harfin seçimlik olduğunu gösteriyor. Yani biz tarayıcı üzerinden “/abcd” de yazsak “/acd” de yazsak yine bu route çalışacak demektir. Dördünce route tanımında ise URL kısmında “/de+fg” ifadesi kullanıldı. Buradaki + işaretinin anlamı ise kendinden önceki gelen harfin istediği kadar çoğaltılabileceği anlamına geliyor. Yani “/defg” de yazsak “/deeeeeefg” de yazsak bu route çalışacak. Beşinci routing tanımında ise URL kısmında “/hi*jk” ifadesi kullanıldı. Buradaki * işaretinin anlamı araya istediğiniz yazabilirsiniz demek. Yani “/hijk” yazsakta “/hi123456jk” yazsakta bu route çalışacak demektir.  Altıncı ve son route tanımına bakacak olursak burada da URL içerisinde degisken1 ve degisken2 diye iki tane degişken tanımı bulunmaktadır. Değişken tanımları önlerine konan : işareti ile belirtilmektedir. URL içerisindeki bu değişkenlere ne yazılırsa Program içerisinden onlara erişebiliriz demektir. Bu değişkenlere route tanımının callback fonksiyonundaki req değişkeninin params değişkeni ile erişebiliriz. Örneğin URL  “/sayfa/111/parametre/222” yazdığımızda bu değişkenlere “req.params.degisken1” yada “req.params.degisken2” şeklinde erişebiliriz. Bütün bu route tanımlarının çıktısını yukarıdaki Gif üzerinde görebilirsiniz. Express Framework’ün route yapısı hakkında daha fazla bilgiye bu adresten ulaşabilirsiniz.

Sabit Dosyalar ( Static Files )

Bir önceki bölümde hatırlarsanız yazılan Url’e göre dosya döndürmeyi öğrenmiştik. Aynı işlemi express üzerinde route tanımlayarak da yapabiliriz. Ama büyük projelerin çoğunda onlarca css, js ve resim dosyaları vardır. Hepsi için tek tek route tanımlaması mı yapmamız gerekiyor? Tabiki hayır. Express Frameworkün yazarları bunu düşünmüş ve bunun için Statik Files özelliğini yazmışlar. Bir örnek üzerinden gidelim. Kodlarımız şöyle:

var express=require('express');
var app=express();

app.get('/',function (req,res) {
    res.send('Ana Sayfa!');
});

app.use(express.static('public'));

app.use('/js',express.static('static'));

app.listen(3000,function () {
    console.log('Uygulama 3000 portunda çalışmakta.')
});

9-4-express-static

Örneğimizin ilk bölümünde daha önceki örneklerde gördüğümüz gibi express modülünü çağırıp app isminde bir uygulama oluşturuyoruz. Devamın bir route tanımlaması yazpıyoruz. Son bölümde de hangi port üzerinde çalışacağını belirliyoruz. Buraya kadar herşey aynı. Farklı olan 8. ve 10. satırlar. 8. satırda uygulamamızda public klasörünün dışarıdan direkt olarak erişilebileceğini ifade ediyoruz. Yani public klasörü içerisinde bulunan bütün dosyaları URL’in sonuna ekleyerek çağırabiliriz. (klasörün adını yani public yazmadan). 10. satırda ise sanal klasör kullanıyoruz. Yani static klasörünün içerisindeki herşeyi dışarıdan erişime açıyoruz.(klasörün adını yani static yazmadan). Ama bu dosyalara erişirken başına js yazarak erişiyoruz. Yukarıdaki gifte kodun çalışır hali dosya sistemi ile gösterilmektedir. Sizlerde denemeler yaparsanız aklınızda daha net oturacaktır.

Bir sonraki bölümde middleware kavramına ve express generator kavramlarına değineceğiz.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

NodeJS Adım 8 – Web Server

Adım Adım Nodejs yazı dizimizin bir önceki bölümünde HttpClient oluşturmayı yani bir web sayfasına bağlanmayı ve nodejs üzerinde olay(event) kullanmayı gördük. Bu bölümdede tam tersi Http server oluşturmayı göreceğiz. Yani bilgi alan değil bilgi veren olacağız, bağlanan değil bağlanılan olacağız.

Server oluşturmak için yine nodejs’in http modülünü kullanacağız. İlk örneğimizde server’ımıza bağlananlara o anki saati gösteren bir uygulama yapacağız. Kodlarımıza geçelim:

var http = require("http")

var server = http.createServer(function (request, response) {
    var tarih = new Date();
    var formatliTarih = tarih.getDate() + "." + (tarih.getMonth() + 1) 
    + "." + tarih.getFullYear();
    formatliTarih += " " + tarih.getHours() + ":" + tarih.getMinutes();
    console.log(formatliTarih)
    response.end(formatliTarih)
})
server.listen(8080)
console.log("Server Başlatıldı. Tarayıcı üzerinden http://localhost:8080"
           +" adresinden ulaşabilirsiniz.")

8-1-node-server

Yukarıdaki kodda çok basit bir server örneği var. Adım adım açıklayalım. İlk satırda http modülünü çağarıyoruz. Bir sonraki satırda http.createServer(…) fonksiyonu ile yeni bir server oluşturuyoruz ve bunu server değişkenine atıyoruz. createServer fonksiyonuna callback fonksiyonu olarak verdiğimiz fonksiyonu inceleyelim. Bu fonksiyon server’a her çağrı yapıldığında çalışır. request ve response olmak üzere iki parametre alıyor. request server’a yapılan istek hakkında bilgi almak için kullanılıyor, response ise yapılan isteğe cevap döndürmek için kullanılıyor. Fonksiyonun ilk satırında bir tarih nesnesi oluşturuluyor. Daha sonraki iki satırda ekrana formatlı olarak tarih yazdırmak için formatliTarih değişkeni oluşturup değerini atıyoruz. Daha sonraki satırda konsol ekranına formatliTarih nesnesini yazdırıyoruz. Bir sonraki satırda response nesnesini kullanarak yapılan isteğe cevap döndüriyoruz. Sonraki satırda yani server.listen(8080) kodu ile oluşturmuş olduğumuz serverı 8080 portu üzerinden başlatıyoruz. Sonraki satırda da konsola serverımızın başladığını haber veren bir yazı yazıyoruz. Bu kadar. Bu kodu çalıştırdığımızda server nesnesi başlamış ve kendisine yapılan her isteğe cevap verebilir halde olacak. Her yapılan istekte callback fonksiyonu çalışacak ve geriye o anki tarih saati bizim belirlediğimiz formatta geri dönderecek. Servera ulaşmak için herhangi bir tarayıcı üzerinden http://localhost:8080 yada 127.0.0.1:8080 yazmamız yeterlidir.

NOT: Konsolda programı çalıştırdığınızda server sürekli çalışacaktır. Kapatmak için CTRL + C tuşlarını kullanabilirsiniz.

Gelelim diğer örneğimize. Bu örneğimizde de geriye bir html dosya içeriğini döndereceğiz. Kodlarımız şu şekilde:

program.js dosya içeriği:

var http = require("http")
var fs=require("fs")

var server = http.createServer(function (request, response) {
    fs.createReadStream("ornek.html").pipe(response)
})
server.listen(8080)
console.log("Server Başlatıldı. Tarayıcı üzerinden http://localhost:8080"
           +" adresinden ulaşabilirsiniz.")

ornek.html dosya içeriği:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Örnek Html Dosyası</title>
</head>
<body>
    <h1>Örnek Html Dosyası</h1>
</body>
</html>

8-2-node-server

İki dosya içeriğimizde yukarıdaki gibi. Html dosyasının içeriği basit zaten. Açıklamaya gerek yok. program.js dosyasının içerisine bakacak olursak bir önceki örnekten ilk farklı ikinci satırda fs modülümüzü çağırıyor olmamız. Diğer farklı ise calback fonksiyonu. Callback fonksiyonunda fs modülünün createReadStream fonksiyonunu kullanıyoruz. Bu fonksiyon parametre olarak okuyacağı dosyanın adını alıyor ve bir okuma işlemi başlatıyor. Fonksiyonun devamındaki pipe fonksiyonu ise şu demek. İlk işlemin çıktısını al ikinci işleme ver. İlk işlem yani ornek.html dosyasının okunma işleminin çıktısı yani ornek.html dosyasını al sonraki işlem yani pipe fonksiyonuna parametre olarak verilen response nesnesine ver. Tabi response nesneside dosyayı alıp cevap olarak gönderecek. Cevabı yani html dosyasını alan tarayıcıda html içeriğini işleyip ekranda gösterecek. Kodlarımız bu kadar. Sonuçta yukarıdaki gifte görüldüğü gibi.

Bir sonraki örneğimizde gelen URL’e göre dosya göndereceğiz. Örnek kodlarımız şu şekilde:

var http = require("http")
var url = require("url")
var fs = require("fs")

var server = http.createServer(function (request, response) {
    var urlObject = url.parse(request.url)
    if (urlObject.pathname == '/ornek1') {
        fs.createReadStream('ornek1.html').pipe(response)
    } else if (urlObject.pathname == '/ornek2') {
        fs.createReadStream('ornek2.html').pipe(response)
    }
    else {
        fs.createReadStream('ornek.html').pipe(response)
    }
})
server.listen(8080)
console.log("Server Başlatıldı. Tarayıcı üzerinden http://localhost:8080"
           +" adresinden ulaşabilirsiniz.")

8-3-node-server

Kodlarımıza bakacak olursak kullandığımız 3 tane modülümüz var: http, url ve fs. Sonrasında daha önce öğrendiğimiz server oluşturma işlemini yapıyoruz. Asıl incelememiz gereken yer callback fonksiyonunun içerisi. Callback fonksiyonunun ilk satırında bize gönderilen yani kullanıcının tarayıcıya yazmış olduğu urli parse(ayrıştırma) işlemine alıyoruz ve urlObject değişkenine atıyoruz. Böylece url hakkında herşeye daha sade olarak erişebiliriz. Daha sonrasında basit if deyimleriyle url’ye yazılan değerleri kontrol ediyoruz. Ben burada sadece iki tanesini kontrol ettim. İsterseniz daha fazlada yapabilirsiniz. Hatta isterseniz gelen değerle aynı isimde dosyaları döndürebilirsiniz. Ben basit olması açısından o konulara girmeyeceğim. Bu if ifadelerinin içerisinde de url değerine göre daha önce gördüğümüz gibi kullanıcıya dosya gönderiyoruz. En son else kısmında da geriye kalan her ihtimal için ornek.html dosyasını gönderiyoruz. Bu kadar. Html ve css biliyorsanız artık nodejs ile basit bir site yapabilirsiniz 🙂

Gelelim diğer ve bölümün son örneğine. Bu örnekte bir önceki bölümde kullandığımız hava durumu servisi gibi bir servis yazacağız. Tabi biz hava durumunu bilemeyeceğimiz için daha basit bir servis yazacağız. Bildiğimiz en basit şey tarih/saat. Servisimize /tarih yazınca gün, ay, yıl , /saat yazında saat, dakika, saniye döndüren bir servis yazacağız. Kodlarımız şu şekilde:

var http = require("http")
var url = require("url")

var server = http.createServer(function (request, response) {
    var urlObject = url.parse(request.url)
    var zaman=new Date()


    if (urlObject.pathname == '/tarih') {
        var tarih={ 
            yil:zaman.getFullYear(),
            ay:zaman.getMonth()+1,
            gun:zaman.getDate() 
        };
        response.end(JSON.stringify(tarih))
    } else if (urlObject.pathname == '/saat') {
        var saat={ 
            saat:zaman.getHours(),
            dakika:zaman.getMinutes(),
            saniye:zaman.getSeconds() 
        };
        response.end(JSON.stringify(saat))
    }
    else {
        response.writeHead(200, {"Content-Type": "text/html; charset=utf-8"});
        response.write("Lütfen saati öğrenmek için /saat ,")
        response.write("tarihi öğrenmek için /tarih adreslerini kullanınız.")
        response.end()
    }
})
server.listen(8080)
console.log("Server Başlatıldı. Tarayıcı üzerinden http://localhost:8080"
           +" adresinden ulaşabilirsiniz.")

8-4-node-server

Kodlarımızı incelediğimiz zaman genel olarak bir önceki örnek ile aynı. Farklı tarafı callback fonksiyonu içerisindeki if yapısı. Bir önceki örnekteki gibi gelen url bilgisini kontrol ederek işlem yapıyoruz. Eğer url /tarih ise tarih adında bir nesne oluşturuyoruz. İçerisine yil, ay, gun değişkenlerini ve değerlerini giriyoruz. Daha sonra JSON.stringify(…) ile nesnemizi json’a çeviriyoruz ve response.end(…) ile dönüştürmüş olduğumuz değişkeni geri döndürüyoruz. Sonraki else if kod bloğunda url /saat ise yeni bir saat değişkeni oluşturup içerisine saat, dakika, saniye değişkenleri ve değerlerini yerleştiriyoruz. Ve json nesnesine çevirip kullanıcıya döndürüyoruz. Ve son else bloğunda ise kullanıcı bu iki url dışında herhangi birşey yazmışsa veya boş bırakmışsa kullanıcıya bilgilendirme metni yolluyoruz. İlk olarak response.writeHead(…) fonksiyonu ile yollayacağımız metnin türünü ve kodlama standardını belirtiyoruz. Bu kodları eklemez isek Türkçe karakter problemi yaşayabiliriz. Daha sonraki iki satırda response.write(…) fonksiyonu ile kullanıcıya bilgilendirme yazısı yolluyoruz. İstersek bunu tek satırda da yollayabiliriz. Son olarakta response.end() ile işlemlerimizi bitirip kullanıcıya yaptığımız işlemleri döndürüyoruz. İşte bu kadar. Artık önceki bölümdeki gibi başkalarının kullanabileceği bilgilendirici API servisleri yapabiliriz.

Bir sonraki bölümde express framework kullanımına başlayacağız.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

NodeJS Adım 7 – HttpClient ve Events(Olaylar)

Adım Adım Nodejs yazı dizimizin bir önceki bölümünde npm yani nodejs package manager’dan ve paket yönetiminden bahsetmiştik.

Hatırlarsanız daha önceki bölümlerde de callback fonksiyonundan bahsetmiştik. Callback fonksiyonu sayesinde asenkron işlemlerin bitiminde yapmamız gerekenleri belirtebiliyorduk. Peki asenkron işlemler sırasında “bitmek” olayı dışında başka olaylar meydana geldiğinde callback fonksiyonu gibi bir fonksiyonla o olay sırasında yapılması gereken işlemleride belirtebilir miyiz? Tabikide yapabiliriz. nodejs’in olaylar özelliği sayesinde meydana gelen her olayda bir fonksiyon çalışmasını sağlayabiliriz.

Bu bölümde örnek olarak bir web sayfasını çağıracağız ve onun üzerinde meydana gelen olaylara fonksiyon atayacağız. Bunun için kullanacağımız modül nodejs’in kendi modülü olan http modülü. Bu modül sayesinde http protokolü ile alakalı neredeyse her işlemi yapabiliriz. Nodejs’in en güçlü olduğu yanlardan bir tanesi web. Bunu sağlayan en önemli parçalardan bir taneside http modülü. Nodejs’in hem asenkron mimarisi, hem olay bazlı yapısı, hemde http modülü birleşince nodejs’in web üzerinde ölçeklenebilir web siteleri, web uygulamaları ve api yazmak çok cazip hale geldi.

Http modülünü kullanan ilk örneğimiz şu şekilde:

 

 var http = require("http")

http.get("http://www.emrekacan.net",function (response) {
    response.setEncoding('utf8')
    response.on('data',function (data) {
        console.log(data)
    })
})

7-1-node-httpclient

İlk satırda nodejs’in global http modülünü çağırıp http değişkenine atıyoruz. Daha sonra http modülünün get fonksiyonu ile http://www.emrekacan.net adresine istekte bulunuyoruz. Yani sanki web tarayıcısından girer gibi bu adrese http modülünün get metodu ile giriyoruz. İstek işleminin bitiminde de callback fonksiyonunu çalıştırıyoruz. Callback fonksiyonumuza tek parametre dönderiliyor. Oda response yani cevap değişkeni. response değişkeni ile yapmış olduğumuz isteğe verilecek cevap hakkında bilgi alabiliriz. Callback fonksiyonunun ilk satırında setEncoding ile yazı formatımızı uft8 olarak belirliyoruz. Yazılarımız bozuk çıkmasın diye. Geldik asıl yere. response değişkeninin on metodu ile bize gönderilecek cevap ile ilgili olaylar meydana geldiğinde işlemler yapabiliriz. Örneğimizde response.on(‘data’,…) şeklinde yazmış olduğumuz ifade şu anlama geliyor: response değişkeninde her data olayı gerçekleştiğinde şu fonksiyonu çalıştır. data olayı nedir peki? Biz bir web adresine istekte bulunduğumuzda web adresi bize tek seferde teslim edilmez. Parça parça teslim edilir. İşte her bir parça bize teslim edildiğinde data olayı çalışır. Yani data olayı data teslim edildi olayıdır. Buradaki callback fonksiyonu da her data teslim edildiğinde çalışır ve bu datayı konsola yazar.

Burada şöyle bir soru gelebilir aklınıza: E hani callback fonksiyonu dediğimiz şey işlem bitince çalışıyordu. http.get() fonksiyonunun callback fonksiyonuda web sayfasının yüklenmesi bitince çalışması gerekmiyor mu? Eğer bitince çalışıyorsa bitmiş birşeyin data olayına nasıl erişiyoruz? Şöyle cevaplayayım. Siz bir tarayıcı çalıştırıp adres bölümüne adresi yazdığınızda sizin web sayfası çağırma işleminiz bitmiş oluyor. Daha sonra o web sayfasının yüklenme işlemi başlıyor. Burada da aynı mantık var aslında. Siz http.get(…) fonksiyonunu kullandığınızda bu fonksiyon belirtilen adresi çağırıyor. Çağırma işlemi bitiyor. Daha sonra sayfanın yüklenme işlemi başlıyor. Bu sırada da bizim http.get(…) fonksiyonuna yollamış olduğumuz callback fonksiyonu çalışıyor. Aslında http.get(…) fonksiyonunun callback fonksiyonuna yollanan response parametresi de başlayan bu yüklenme işlemi zaten. Bu sayede yüklenme işleminin data fonksiyonuna erişebiliyoruz.

Peki bu response’un başka olayları yok mu? Tabiki var. Bi örnek daha yapalım:

 

var http = require("http")

http.get(process.argv[2], function (response) {
    response.setEncoding('utf8')
    response.on('data', function (data) {
        console.log(data)
    })
    response.on('end', function () {
        console.log('Yüklenme işlemi bitti.')
    })
    response.on('error', function (hata) {
        console.log('Yüklenme sırasında bir hata oluştu:' + hata.message)
    })
})
.on('error', function (hata) {
    console.log("Web adresinin açılması sırasında bir hata oluştu." + hata.message)
})

7-2-node-httpclient

Bu örnek bir önceki örnekle hemen hemen aynı. Bu örnekte web adresini konsoldan parametre olarak alıyoruz. Ek olarakta iki tane daha olay ekledim. Birisi end olayı. Adında anlaşılacağı üzere yükleme işlemi bittiğinde tetiklenen olaydır. Diğeri ise error olayı. Buda herhangi bir hata olduğunda tetiklenen bir olaydır. En altta http.get(…) fonksiyonunun dışında bir olay daha var. Oda error olayı. Buda herhangi bir hata anında tetiklenen bir olay. Peki iki error olayının farkı ne? Aslında yazıldıkları yerlere bakınca farkı anlayabiliriz. http.get(…) fonksiyonunun içerisine yani response’a yazılan error olayı yükleme sırasında bir hata oluştuğunda tetiklenir. http.get(…) fonksiyonunun dışına yazılan error olayı ise istek sırasında herhangi bir hata oluşursa tetiklenecek olaydır.

Olaylar yardımı ile bir web adresine istekte bulunup cevabı almayı gördük. Peki bu bizim ne işimize yarayacak? Gelen html içeriği xml parser araçları yardımı ile parse edebilirsiniz yani ayıklayabilirsiniz ve içerisinde işinize yarayabilecek verileri alabilirsiniz. Ya da belirli sürelerde web sitelerinin arşivini yapmak isteyebilirsiniz. Bunlar gibi şeyler yapılabilir ama en mantıklısı API’ları kullanmaktır. API’lar yine http protokolü ile çalışan URL’ler üzerinden ulaşabildiğimiz bize daha net bilgi veren json veya xml formatında kullanılabilen web servisleridir. Örneğin hava durumunu bize sunan bir API düşünün. Siz URL’inizi yazıyorsunuz, nerenin hava durumunu öğrenmek istediğinizi yazıyorsunuz ve size kısa bir json nesnesi dönüyor. Sizde ano alıp işliyorsunuz. Bu kadar. İşte bu basit bir API örneği. Bizde örneğimizde bunu kullanacağız. Diğer örneğimize geçelim.

Bu örnekte openweathermap.org sitesinin API larını kullanacağız. Belirli bir sınıra kadar ücretsiz. Örnek için yeterli. Bunun siteye üye olmanız gerekiyor. Üye olduktan sonra API Keys bölümünden yeni bir API Key oluşturabilir veya varolan API Key’i kullanabilirsiniz.

7-4-httpclient

Tabi bu API’lerin nasıl kullanılacağını dokümantasyonundan öğreniyoruz. Dokümantasyonu incelediğimizde belirtilen yerin şuan ki hava durumunu öğrenmek için şöyle bir URL yeterli olacaktır : http://api.openweathermap.org/data/2.5/weather?q=YER&appid=APIKEY  . YER yerine Ankara, İstanbul, Kayseri gibi bir yer ismi, APIKEY yerinede oluşturmuş olduğunuz API Key’lerden bir tanesini yerleştirin. Bunu herhangi bir tarayıcıdan çalıştırdığınızda size JSON bir değer dönderecektir. İşte biz bu JSON nesneyi Kullanacağız. Bir sonraki örnekte Konsoldan il adını alıp ekrana hava durumu sonucunu yazan bir uygulama yapacağız. Kodlarımıza geçelim:

 

var http = require("http")

http.get('http://api.openweathermap.org/data/2.5/weather?q=' + process.argv[2] + 
'&units=metric&appid=02583a7bfd3cd5d870c5fa483017fd61', function (response) {
    response.setEncoding('utf8')

    var sonuc = '';

    response.on('data', function (data) {
        sonuc = sonuc + data;
    })
    response.on('end', function () {
        var jsonSonuc = JSON.parse(sonuc);
        console.log(jsonSonuc.main.temp)
    })
    response.on('error', function (hata) {
        console.log('Yüklenme sırasında bir hata oluştu:' + hata.message)
    })
})
.on('error', function (hata) {
    console.log("Açılma sırasında bir hata oluştu." + hata.message)
})

7-3-node-httpclient

İnceleyecek olursak ilk satır http modülünün çağırılarak http değişkenine atıyoruz. Bir sonraki satırda http.get(…) fonksiyonuna API’ın URL’ini yazıyoruz. URL içerisindeki process.argv[2] bölümünde ise konsoldan aldıdığımız il adını URL içerisine ekliyoruz. URL’in sonundaki appid bölümündeki Key alanını siz kendi keyiniz ile değiştirin. Daha sonra callback fonksiyonumuz çalışıyor. Callback fonksiyonu içerisinde sonuc isminde bir değişken oluşturdum. Daha sonra data olayında gelen dataları sürekli olarak sonuc değişkenine ekledim. (Normalde çok kısa bir cevap olduğu için tek seferde geliyor. Ama önlem olarak değişkene atmak faydalı.) end olayında ise elde ettiğim sonuc değişkenini JSON’a dönüştürdüm ve jsonSonuc değişkenine attım. Son olarakta ekrana jsonSonuc nesnesinin main.temp değerini yazdırdım. Hava durumu sonucunun main.temp içerisinde olduğunu nerden biliyorum? Tabiki dokümantasyondan. Onun dışında API URL’ini tarayıcı üzerinden çalıştırarak da bakabilirsiniz. İşte bu kadar. Artık konsol üzerinden hava durumu öğrenebileceğimiz bir programımız var.

HttpClient ve Olaylar konumuzda bu kadar. Bu bölümde bir web sayfasını nasıl çağırabileceğimizi, çağırdıktan sonra yüklenme işlemine nasıl erişebileceğimizi ve bu yüklenme işlemindeki olaylara nasıl erişebileceğimizi gördük.

Bir sonraki bölümde kendi web sayfalarımızı nasıl yapabileceğimizi göreceğiz.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

NodeJS Adım 6 – Npm paket yöneticisi

Adım Adım Nodejs yazı dizimizin bir önceki bölümünde modül oluşturmayı ve bunları kullanmayı öğrendik.

Amacımız sürekli aynı kodları yazmamak ve tekrar kullanılabilir program parçaları üretmekti. Peki yazdığımız bu modülleri başkalarının kullanmasını sağlayabilir miyiz? Yada başkalarının yazdığı modülleri biz kullanabilir miyiz? Yada kullandık diyelim. Daha sonra bu modülün sahibi paketi güncellediğinde kolayca güncelleyebilir miyiz? (Bu arada paketi modülün paketlenmiş kullanıma hazır hali gibi düşünebilirsiniz. Aslında aynı şeyler.)

Tabiki. Bunu npm yani node package manager ile çok kolay bir şekilde yapabiliriz. npm nodejs’i yüklediğimizde yüklenen bir araç. npm ile oluşturmuş olduğumuz modülleri başkalarının kullanması için yayınlayabiliriz. npm resmi olarak https://www.npmjs.com adresini kullanır. Yayınlamış olduğumuz paketlere herkes bu adresten erişebilir. Yine npm ile herkesin açık olarak yayınladığı paketlere bizde buradan ulaşabiliriz. Ben bu yazıyı yazarken toplam paket sayısı 330338 adet idi. Paket yayınlamaya çok girmeyeceğim. Gerekli dokümantasyona buradan ulaşabilirsiniz.

npm ile Paket Yükleme

npm tamamen konsol üzerinden kullanılan bir araç. npm ile tüm işlerimizi konsol komutları ile yapacağız. Paket yüklemek için kullanacağımız komut ise şu şekilde: npm install paketadi  . Bu komut ile npm üzerindeki bütün paketlere ulaşabiliriz. Tabi bu paketlerin ne işe yaradıkları ve nasıl kullanıldıklarını paketlerin dökümantasyonundan öğreneceğiz. Örnek olarak lodash paketini yükleyelim. Bunun için konsol ekranına npm install lodash  yazmamız yeterli. Bu komut npm’nin deposundan lodash paketini bizim için indirecektir. Peki ama nereye yükleyecek? npm ile yüklediğimiz paketler bulunduğumuz klasör içerisindeki node_modules adındaki bir klasöre yüklenir.(Klasör yoksa oluşturulur.) Bu paketleri kod içerisinde nodejs’in var olan paketleri gibi kullanabiliriz. Örneğin lodash paketini kullanmak için var ldsh=require(“lodash”);  yazmamız yeterli. Nodejs bu paketi node_modules klasörü içerinde bulup ldsh değişkenine atacaktır. Lodash içerisinde bütün nesne ve fonksiyonlarıda ldsh değişkeni ile erişebilir.

6-1-node-npm

İlk paketimizi yüklemiş olduk. Aslında npm ile sadece nodejs paketleri yüklemiyoruz.Nodejs paketleri dışında css paketleri, javascript paketleri(ön yüz geliştirme için) … gibi paketlerde yükleyebiliriz. Mesela bootstrap css frameworkünü npm ile yükleyebiliriz. Bunun için şu komutu vermemiz yeterli npm install bootstrap . Aynı şekilde jquery javascript kütüphanesini yüklemek istiyorsak şu komutu vermemiz yeterlidir: npm install jquery . npm’nin bir diğer yeteneği de yüklemek istediğimiz paketlerin istediğimiz versiyonunu yükleyebiliyor olmamız. Mesela jquery’nin 1.11.2 versiyonunu yüklemek istiyorsak şu komut bizim için yeterli olacaktır: npm install jquery@1.11.2  .Geçmiş versiyonlar gibi alfa veya beta versiyonlar içinde yükleme yapabiliriz. Bütün bu versiyonlar tabiki paket sahibi tarafından belirleniyor. Yükleyeceğimiz paketin çalışması için başka bir paket yüklenmesi gerekiyorsa npm bizim için onları da yüklüyor. Mesela jquery validation paketinin çalışması için jquery paketininde yüklenmesi gerekiyor. Siz npm install jquery-validation  komutunu çalıştırdığınızda npm jquery validation paketiyle beraber jquery paketini zaten yüklemiş oluyor.

package.json Dosyası ve Maharetleri

Peki bizim projemiz için toplam paket sayısı 50yi geçti diyelim. O zaman bizim projemizin boyutu çok yükseleceği için taşınabilirliği düşecektir. Projeyi iş arkadaşımıza vereceğiz diyelim. 50 paketin hepsiyle beraber kopyalayıp vereceğiz. Yada diyelimki kodlarımızı versiyon kontrol sistemine yüklüyoruz. Ve sürekli versiyonlama yapıyoruz. Yükleme ve versiyonlamanın tamamı 50 paket ile birlikte olacak. Buda çok zaman alacaktır. Bu işin kolay bir yolu yok mu? Tabiki var. package.json dosyası.

package.json dosyası projemizin ana dizininde bulunan ve projemiz hakkında bilgiler içeren, projemiz içerisinde kullanılan paketlerin listesini tutan, projemiz için konsolda kullanılabilecek komutlar için kısa yollar eklenebilen ve daha fazlasını içeren özel bir json dosyasıdır. Örnek bir package.json dosyası şu şekildedir:

{
  "name": "proje_adi",
  "version": "1.0.0",
  "keywords": [
    "nodejs",
    "npm",
    "ders",
    "ornek"
  ],
  "description": "proje açıklaması",
  "main": "program.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Emre KAÇAN",
  "license": "MIT",
  "dependencies": {
    "bootstrap": "^3.3.7",
    "jquery-ui": "^1.12.0",
    "lodash": "^4.15.0"
  }
}

Dosyamızın içeriğine bakacak olursak name alanı projemizin adı, version alanı projemizin şuan hangi versiyonda olduğu, keywords alanı projemiz hakkındaki anahtar kelimeleri, description alanı projemiz hakkında açıklama, main alanı projemizi çalıştırmaya hangi dosyadan başlanacağını, scripts projemiz için konsolda çalıştırabileceğimiz komutların kısaltmaları, author alanı proje yazarı/sahibi, license proje lisansı ve son olarak da dependencies projemizin bağımlılıkları yani projemizde kullandığımız paketlerin listesi hakkında bilgi verir.

Projemizin ana dizinine bir dosya oluşturup adına package uzantısına da .json vererek bu dosyayı oluşturabiliriz. İçeriğini yukardaki gibi kendimiz yazabiliriz. Ama bunun daha kolay bir yolu var. konsoldan şu komutu vermemiz yeterli npm init . Bu komutu verdiğimizde bize birkaç soru sorulacak ve sorular bittiğinde dosya kendisi oluşacak. Daha önceden yüklemiş olduğunuz paketler varsa onlarıda dependecies yani bağımlılıklar yani paket listesine ekleyecektir. Soru soruların hepsine cevap vermek zorunda değilsiniz. Hiçbirine cevap vermeyip sadece paketleri algılamasını sağlayabilirisiniz.

6-2-node-npm

Dosyamızı oluşturuk. Bu dosya projemiz hakkında bir çok bilgi barındırıyor. En önemliside paket listesi. Artık projemizi taşırken node_modules klasörünü silebiliriz. Yada projemizi versiyon kontrol sistemlerine atarken node_modules klasörünü hariç tutabiliriz(exclude). Peki ama projeyi alan başka bir kişi paketleri tekrar nasıl yükleyecek? Çok basit şu komutla: npm install . Biz bu komutu konsola yazdığımızda npm package.json dosyasına bakacak ve içerisindeki paketleri yükleyecek. Özellikle github üzerinden indirdiğiniz veya checkout yaptığınız projelerin çoğunda projeyi çalıştırmadan önce bu komutu vermeniz gerekir.

package.json içerisindeki scripts kısmı için projemiz için konsolda kullanacağımız komutların listesini tutabiliriz demiştik. Mesela programımızı konsolda çalıştırmak için node program.js komutunu veriyorduk. Bunu package.json içerindeki script kısmına ekleyelim.

 

{
  ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node program.js"
  },
  ...
}

Artık projemizi npm ile başlatabiliriz. Konsola şu komutu vermemiz yeterli : npm start 

Bir scripte birden fazla komutta yazabiliriz. package.json dosyamızı şu şekilde değiştirelim:

 

{
  ...
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "npm install && node program.js"
  },
  ...
}

Konsola tekrar npm start  yazdığımızda npm önce npm install  komutunu çalıştırıp paketleri yükleyecek, daha sonra node program.js  komutunu çalıştırıp programı çalıştıracak.

npm ile ilgili söyleyeceklerimiz bu kadar değil ama ilk aşama için bu kadar yeterli sanırım. Gerisini keşfetmek size kalmış.

npm ile Global Paketler ve Küçük Araçlar

npm ile paket yüklemek bu kadar kolayken bu kolaylığı başka yerlerde kullanmamak olmazdı. npm ile konsolda kullanabileceğimiz küçük araçlarda yükleyebiliriz. Mesela resimlerinizi yeniden boyutlandıran bir konsol aracı düşünün. Siz aracın adını yazıyorsunuz, resmin adını yazıyorsunuz, boyutu yazıyorsunuz ve o araç sizin için o resmin boyutunu büyütüyor veya küçültüyor. Büyük kolaylık. Tek satır komut ile işimizi halletmiş oluyoruz. Bunun için npm bize yeter. Ama npm yüklediği paketleri node_modules klasörüne atıyordu. Oradaki bir araç nasıl olacakta konsolda çalışır hale gelecek? Tabiki oraya yüklemeyerek. npm ile istediğimiz paketleri global olarak yükleyebilir. Mesela yukarıda bahsettiğim örnek için paketimizin adı resize-img-cli. resize-img-cli paketini global olarak yüklemek için konsola şu komutu vermemiz yeterli: npm install –global resize-img-cli  . Yada npm install -g resize-img-cli  . İki komutta aynı. Bu komut ile resize-img-cli paketini bilgisayarımızda bulunan global npm deposuna yüklüyor. Ve biz resize-img-cli paketini konsol üzerinden istediğimiz gibi kullanabiliyoruz. Mesela resize-img-cli ile bir resmin boyutunu değiştirmek için resize-img-cli resim1.jpg –width 200 > resim2.jpg  komutunu çalıştırmamız yeterlidir. Bu komut npm’nin global deposundaki resize-img-cli aracını çalıştıracak ve parametre olarak verdiğimiz resim1.jpg dosyasını alacak ve boyutunu değiştirip resim2.jpg adında yeniden kaydedecek. (Bu aracı kullanmayı tabiki dokümantasyonundan öğreniyoruz.) Bu kadar.

6-3-npm

Paket yüklemeyi ve global paketlerle küçük araçlar kullanmayı bu kadar kolaylaştırdığı için npm’nin şuan yüzbinlerce paketi var. Ve çok popüler olarak kullanılmakta. İnsanlar nodejs kullanmasa bile npmyi bir şekilde kullanıyorlar. Çünkü npm araçları sadece nodejs değil nodejs dışında birçok işe yarıyor. Birkaç örnek vermek gerekirse yeoman paketi ile proje taslakları oluşturabilirsiniz, gulp paketi ile projenizde yaptığınız sürekli görevleri otomatize edebilir, less paketi ile .less dosyalarınızı derleyebilirsiniz… gibi daha birçok küçük araçlar işleriniz kolayca halledebilirsiniz.

npm ile anlatılabilecekler tabiki bu kadar değil. Ama bunlar sanırım yeterli olacaktır. Daha fazla bilgi ve paket için npmjs.com adresine bakabilirsiniz.

Bir sonraki bölümde nodejs’e geri dönüp httpClient ile web sitelerine ve servislerine bağlanmayı ve olayları anlatacağım.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

NodeJS Adım 5 – Modüler Programlama

Adım Adım Nodejs yazı dizimizin bir önceki bölümünde asenkron programlamadan ve callback fonksiyonundan bahsettik. Bu bölümde de Modüler Programlama ile devam edeceğiz.

Program yazarken kodlarımız bazen 10 bazen 100 bazen 1000 bazen binlerce satıra ulaşabilir. Büyüdükçe de yönetimi zor içinden çıkılmaz bir hale gelir. Ve zaman zaman aynı kodları sürekli sürekli yazarız. Bunu önlemek için sürekli sürekli yazdığımız kodları fonksiyonlara taşırız. Ama kodlarımız çok olduğunda buda yetersiz gelir. Tekrarlayan fonksiyonlar ortaya çıkabilir. Bu seferde fonksiyonlar çok fazla olur ve onlar yönetilemez, içinden çıkılamaz bir hale gelir. Bunu önlemek içinde aynı amaca hizmet eden fonksiyonları ayrı ayrı dosyalarda birleştiririz. Örneğin I/O işlemeleri için yazılmış fonksiyonları bir dosyada toplayabiliriz. Çoğu zaman yeterli olsa da programlarımız büyümeye devam ettikçe dosyalarında yönetimi zorlaşabilir. Yüzlerce dosyayı gruplandırıp yönetmek kolay olmayabilir. Bu sorunu da çözmek için belirli amaca hizmet eden dosyalardan oluşan dosya grupları oluştururuz.

Yazdığımız kodları yönetilebilir hale getirmek, kod karmaşasını ortadan kaldırmak ve tekrar kullanılabilir kodlar oluşturmak için mantıksal olarak aynı işleri yapan kod veya dosyaların bir araya getirilerek oluşturulduğu, tek başına çalışamayan ana programlar tarafından kullanılarak çalıştırılan program parçalarına modül(yada kütüphane yada paket) denir. nodejs üzerinde kullanılan bir çok modül bulunmaktadır. Bunlardan bazıları nodejs çalışırken otomatik yüklenir(daha önce kullandığımız process gibi) bazılarını da kodlama sırasında kodlar yardımı ile biz yükleriz(daha önce kullandığımız fs modülü gibi).

Modülün ne olduğunu öğrendiğimize göre artık kendi modüllerimizi nasıl yazacağımıza bakalım. Bu bölümün ilk örneğinde ayrı bir dosyaya kendi modülümüzü yazıp ana dosyamız içerisinde onu nasıl kullanabileceğimize bakacağız. İlk olarak modülümüzü yazalım ve modul1.js isimli bir dosyaya kaydedelim. Kodlarımız şu şekilde :

 

var fs = require('fs');

module.exports = function (dosyaAdi, callback) {
    var dosyaIcerigi = fs.readFile(dosyaAdi, function (hata, data) {
        if (hata) {
            return callback(hata);
        }
        callback(null, data.toString());
    })
}

Basit bir modül yazdık. Bu modülde modüle yollanan dosya ismine göre dosya okuyup içeriğini geri gönderiyoruz. Adım adım bakacak olursak ilk satırda fs modülünü çağırdık. Daha sonra module.exports = function(…) … kodları ile dosyamızı bir modüle çevirmiş olduk. Bu kodlar sayesinde bu dosyayı çağıran her program yazmış olduğumuz modülü bir fonksiyon olarak kullanabilecek. Modül fonksiyonunun içeriğine bakacak olursak parametre olarak iki değer alıyor. Birincisi dosyaAdi adında bir değişken. Bu değişkene modülü kullanan program bir dosya adı yollayacak. Bizde bunu fonksiyon içerisinde kullanacağız. İkinci olarakta callback adında bir değişken alıyor. Evet gelelim en önemli noktalardan birisine. Modüller doğası gereği asenkron çalışırlar. Asenkron çalışan her fonksiyonun bir callback fonksiyonu olacak ki işi ne zaman bitmişse bilelim ve yollamış olduğumuz callback çalışsın. Bizim modülümüzde asenkron ve bizde işimiz bittiğinde bunu kullanıcıya bildirmeliyiz. Bunun içinde bize bir calllback yollanmalı ve bunu program sonunda çalıştırmalıyız. callback değişkeni bunun için var.

Fonksiyonumuz içerisinde fs modülü içerisindeki readFile fonksiyonu ile bize yollanan dosyaAdi değişkeni içerisindeki dosyayı okuyoruz. readFile’ın callback fonksiyonu içerisinde ilk olarak hata kontrolü yapıyoruz. eğer hata varsa bize yollanan callback değişkeninin ilk parametresine veriyoruz ve geri dönderiyoruz. Burası biraz garip gelebilir. Evet callback bir değişken ama sonuçta bize bir fonksiyon yollanacak. Yani callback türü fonksiyon olan bir değişken. Bu yüzden biz onu fonksiyon olarak kullanabiliriz. Devam edelim. Eğer hata yoksa kodlarımız devam edecek. Bir sonraki satırda callback değişkeninin ilk parametresine null, ikinci parametresine okumuş olduğumuz dosyanın içeriğini stringe çevirip veriyoruz. callback fonksiyonunun ilk parametresine null yani boş değer yolladık. Çünkü ilk parametre hata için kullanıldı. Biz yine de ilk parametreye yollayabilirdik. Hata oluşmazdı. Ama kullanıcı ilk parametreden gelen değerin hata mı yoksa dosya içeriğimi olduğunu anlayamazdı. Bazı şeyler biraz havada kalmış olabilir ama modülümüzü kullanırken her şey daha da netleşecek. Gelelim modülümüzü kullanmaya. program.js dosyasındaki kodlarımız şu şekilde:

 

var m1=require('./modul1');

m1(process.argv[2],function (hata,data) {
    if (hata) {
        console.log("Bir hata oluştu.");
        return;
    }
    console.log(data);
});


5-1-node-module

İlk satırda yazmış olduğumuz modülü çağırıp m1 değişkenine atadık. Bizim modülümüz tek bir fonksiyondan oluşuyor. Dolayısıyla m1 değişkeninin türü aslında bir fonksiyon. Yani biz m1 değişkenini bir fonksiyon gibi kullanacağız. Kodlarımıza devam edelim. Modülümüzün ilk değişkeni okuyacağımız dosyanın ismiydi. Biz dosya ismini konsoldan parametre olarak alacağız. Almış olduğumuz bu parametreyi de m1 değişkenine yani modülümüzün ilk parametresine vereceğiz. İkinci parametre olarakta bir fonksiyon yazıyoruz. Bu fonksiyon bizim modülümüzdeki callback değişkenine denk geliyor. Yani modülümüzün işi bittiğinde yada hata olduğunda bu fonksiyon çalışacak. (Modülümüzü hatırlayın.). Bu fonksiyonda ilk olarak hata kontolü yaptık. Daha sonra gelen data değişkenini console ekranına yazdırdık. Evet kodlarımız bu kadar. Daha iyi anlaşılması için modul1.js dosyasını ve program.js dosyalarını yan yana koyarak inceleyebilirsiniz. Tabi şu noktada çok önemli. Bizim modülümüzün ne işe yaradığı, yada callback fonksiyonunun yapısının nasıl olduğu gibi bilgileri bizden başka kimse bilemez. Bunun için her hazırladığımız modülün bir dokümantasyonu olmalı. Ki modülü kullanacaklar bilsin. Sadece biz kullanacak olsak bile aradan zaman geçince unutabiliriz.

Peki fs modülünde olduğu gibi içerisinde birden fazla fonksiyon olan bir modülü nasıl hazırlarız. İkinci örneğimizde Matematik adında bir modül yapacağız. Modülümüzün 4 tane fonksiyonu olacak : Topla, Çıkar, Çarp, Böl. Matematik.js dosyası şu şekilde:

 

var Mat = function () { };

Mat.prototype.Topla = function (a, b) {
    return Number(a) + Number(b);
}

Mat.prototype.Cikar = function (a, b) {
    return Number(a) - Number(b);
}

Mat.prototype.Carp = function (a, b) {
    return Number(a) * Number(b);
}

Mat.prototype.Bol = function (a, b) {
    return Number(a) / Number(b);
}

module.exports = new Mat();

Kodlarımıza bakacak olursak Mat adında bir javascript sınıfı oluşturduk. Bu nesneye Topla, Cikar, Carp, Bol adında 4 tane fonksiyon oluşturduk. Fonksiyon içerikleri basit. Tek dikkat edilecek nokta Number() kullanımı. Bunu fonksiyonu kullanma sebebimiz fonksiyona yollanan parametreleri sayıya çevirmek. Aksi halde javascript hata vermez ama işlem sonucu yanlış olur. En son satırda da oluşturmuş olduğumuz Mat sınıfının bir nesnesini modül olarak atadık. Gelelim modülümüzü kullanmaya. program.js kodları şu şekilde:

 

var m1 = require('./Matematik');

var t1 = m1.Topla(process.argv[2], process.argv[3]);

var t2 = m1.Cikar(t1, process.argv[4]);

var t3 = m1.Carp(t2, process.argv[5]);

var t4 = m1.Bol(t3, process.argv[6]);

console.log(t1, t2, t3, t4);

5-2-node-module

İlk satırda Matematik modülümüzü çağırdık. Daha sonraki 4 satırda konsoldan aldığımız parametreleri kullanarak 4 işlem yaptık. Her birinin sonucunu değişkenlere atadık. En son satırda da bu sonuçların tamamını konsol ekranına yazdırdık. Kodlarımız bu kadar. Dikkat ettiyseniz modülümüz içerisindeki fonksiyonlar asenkron değiller. Biz kodlarımız içerisinde herhangi bir asenkron işlem yapmadığımız için kodlarımızda asenkron olmadı. Önceki örnekte modülümüz içerisinde asenkron bir fonksiyon kullanmıştık. Bu nedenle modülümüz asenkron olmuştu.  

Evet, bu bölümde  modül oluşturmayı ve oluşturmuş olduğumuz modülleri kullanmayı gördük. Aslında 5 farklı modül oluşturma yöntemi var. Biz burada ikisini gördük. Bunlar işimizi rahatlıkla görecektir.

Bir sonraki bölümde bu modülleri daha etkili bir şekilde nasıl kullanabileceğimize bakacağız.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

NodeJS Adım 4 – Asenkron Programlama

Adım Adım Nodejs yazı dizimizin bir önceki bölümünde I/O işlemlerinden yani dosya okuma yazma işlemlerinden bahsettik. Bu bölümde senkron ve asenkron programlamayı anlatacağım.

Senkron Programlama

Yazdığımız programların çoğu yazmış olduğumuz kodları yazılış sırasına göre yukarıdan aşağıya doğru işleyerek ilerler. Her şey sıra ile yapılır. Örneğin bir önceki bölümde yazmış olduğumuz şu kod buna bir örnektir:

var fs=require("fs");
var dosyaIcerigi=fs.readFileSync(process.argv[2]);
console.log(“Program devam ediyor...”);
var satirlar=dosyaIcerigi.toString().split("\n");
console.log(satirlar.length);

5 satırda sırasıyla işler: fs modülünü çağır, dosya içeriğini al, ekrana “Program devam ediyor…” yaz, satırları hesapla, satır sayısını ekrana yaz. Hiçbir işlem birbirinin önüne geçemez. Ve birbirini beklemek zorundadır. Sırayla,  senkron bir şekilde işlemek zorundadır. Bu tip programlama metoduna Senkron Programlama denir.

Asenkron Programlama

Senkron programlamadaki her şeyi sırayla işlemesi ve her bir işlemin birbirini beklemesi yeri geldiğinde programımızı çok yavaşlatabilir, hatta işlem bitene kadar durdurabilir. Örneğin yukarıdaki kodda 3. satır bir önceki satırı yani dosya okuma işlemini beklemek zorundadır. Dosya içeriği çok büyükse bu işlemler dakikalar bile alabilir. Ekrana “Program devam ediyor…” yazdırmak için bir önceki işlemin bitmesini beklemek pek akıllıca değil. İşte bu tip durumlar için asenkron fonksiyonlar kullanırız. Kod akışının sırayla işlemediği, işlemlerin birbirini beklemediği, kod akışının işlem durumlarına göre devam ettiği programlamaya Asenkron Programlama denir.

4-0-node-async

Asenkron işlemler için modüller içerisinde asenkron fonksiyonlar vardır. Örneğin fs modülü içerisinde kullanmış olduğumuz readFileSync metodunun asenkron versiyonu “readFile” metodudur. Yukarıdaki örneğimiz bu metot ile yeniden yazalım.

var fs=require("fs");
var dosyaIcerigi=fs.readFile(process.argv[2]);
console.log(“Program devam ediyor...”)
var satirlar=dosyaIcerigi.toString().split("\n");
console.log(satirlar.length);

 

4-1-node-async

Bu şekilde kodlarımızı asenkron yapmış olduk. Artık 2. satırdaki işlem çalışırken 3,4 ve 5 satırlar onun sonucunu beklemeden devam eder. Ancak yukarıdaki kodlarımız hata verecek. Neden? Çünkü biz 4. satırda 2. satırdaki kodun sonucunu kullanıyoruz. Ama daha 2. satırdaki kodun işi bitmedi.(Eğer okunan dosya çok çok küçükse küçük bir ihtimal bitmiş olabilir.) Peki biz ikinci satırdaki kodun işinin bittiğini nereden anlayacağız? Bittikten sonra yapmamız gerekenleri nerede belirteceğiz? Callback fonksiyonunda.

Callback fonksiyonu

Asenkron fonksiyonlar içerisindeki işlemler bittikten hemen sonra yapılacakların belirlendiği metotlara callback metotları denir. Bu metotlar asenkron metotlara parametre olarak verilir. Sadece bitiminde değil herhangi bir hata sonucunda da callback fonksiyonları kullanılabilir. Bu tamamen asenkron metodun yapısıyla alakalıdır. Nodejs içerisindeki callback fonksiyonlarında genellikle en az iki tane parametre belirlenir. Biri hata durumunu kontrol etmek için, diğeri fonksiyon sonucunu almak için kullanılır. Callback fonksiyonunun kaç tane parametre aldığını, yapısının nasıl olduğunu, hangisi sıra ile yazılacağını … vs bilgileri kullanacağımız modül ve fonksiyonun dokümantasyonundan öğrenebiliriz. Nodejs içerisindeki resmi modüllerin dokümandasyonuna https://nodejs.org/en/docs/ adresinden kullandığınız nodejs versiyonuna göre ulaşabilirsiniz. Örneğin fs modülü içerisindeki readFile metodunun dokümantasyonuna buradan ulaşabilirsiniz.

Gelelim kullanımına. Yukarıdaki kodu şu şekilde düzenliyoruz:

var fs=require("fs");

var callback1 = function (hata,dosyaIcerigi) {
   if (hata) {
       console.log("Bir hata oluştu.")
       return;
   }
   var satirlar=dosyaIcerigi.toString().split("\n");
   console.log(satirlar.length);
}

fs.readFile(process.argv[2],callback1);
console.log("Program devam ediyor...");

4-2-node-async

Kodlarımıza bakacak olursak callback1 adında bir fonksiyon oluşturduk. Bu fonksiyon iki tane parametre alıyor. (Yapısını tabiki dokümantasyondan öğrendik.) Bu fonksiyonu readFile fonksiyonuna parametre olarak verdik. readFile işlemlerini yaptıktan sonra bizim vermiş olduğumuz callback1 fonksiyonunu ilk parametre olarak varsa hatayı, ikinci parametre olarak da hata oluşmamışsa sonucu parametre olarak verecek ve çalıştıracak.

Kodumuza genel olarak tekrar bakarsak, ilk satırda fs modülünü çağırdık. ikindi satırda parametre olarak yolladığımız dosyanın içeriğini okumaya başladık. 2. Satırdaki kodumuz devam ederken 3. satırda ekrana “Program devam ediyor…” yazdık. Diğer taraftan 2. satırdaki kod ne zaman biterse callback1 fonksiyonunu çağıracak. callback1 fonksiyonu içerisinde de ilk olarak hata olup olmadığını kontrol ettik. Varsa konsol ekranına “Bir hata oluştu.” yazdırdık ve programı bitirdik. Yoksa devam edecek. Devam ederse dosyaIcerigi degiskenini string ifadeye çevirip satırlarına ayırdık. Daha sonrada satır sayısını ekrana yazdırdık. Genel olarak programımız bu şekilde işliyor. Aynı programı şu şekilde de yazabiliriz:

var fs = require("fs");

fs.readFile(process.argv[2], function (hata, dosyaIcerigi) {
   if (hata) {
       console.log("Bir hata oluştu.")
       return;
   }
   var satirlar = dosyaIcerigi.toString().split("\n");
   console.log(satirlar.length);
});

console.log("Program devam ediyor...");

Önceki kod ile bu kod arasında hiçbir fark yok. Sadece yazım şekli farklı. İlkinde callback fonksiyonunu bir değişkene atayıp öyle readFile fonksiyonuna ilettik. Diğerinde direkt içerisine yazdık. Hangisi kolayınıza geliyorsa kullanabilirsiniz. İkisinin arasındaki farkı daha iyi anlamak için iki kodu yan yana koyup inceleyebilirsiniz. Javascript ve nodejs dünyasında ikinci kullanım daha yaygındır. Kullanmasanız bile kod okurken ikincisini anlamak faydalı olacaktır.

Gelelim ikinci örneğimize. İkinci bir örnek olarakta dosyanın birinden okuduğumuz içeriği ikinci bir dosyaya yazacağız. Tabi iki dosya isminide parametre olarak alacağız. Okuma işlemi için daha önce kullandığımız fs modülününü readFile fonksiyonunu, yazma işlemi içinde fs modülünün writeFile fonksiyonunu kullanacağız. writeFile fonksiyonuda readFile gibi asenkron bir fonksiyon. (Daha önce kullandığımız writeFileSync fonksiyonunun asenkron versiyonu.) Hem okuma hem yazma işlemlerimiz asenkron olacak. Kodumuz şöyle:

 

var fs=require("fs");

var callback1 = function (hata,dosyaIcerigi) {
    if (hata) {
        console.log("Bir hata oluştu.")
        return;
    }
    fs.writeFile(process.argv[3], dosyaIcerigi, callback2);
    console.log("Dosya okuma işlemi bitti.");
}

var callback2=function (hata) {
    if (hata) {
        console.log("Bir hata oluştu.")
        return;
    }
    console.log("Dosya yazma işlemi bitti.")
}

fs.readFile(process.argv[2],callback1);
console.log("Program devam ediyor...");

4-3-node-async

Kodumuzun ne yaptığına bakalım. İlk olarak fs modülünü çağırıyoruz. Sonra callback1 ve callback2 adında iki tane fonksiyon tanımlıyoruz. Daha sonra (20. satırda) dosya okuma işlemine başlanır. Sonra ekrana “Program devam ediyor…” yazdırdık. Dosya okuma işlemi bittikten sonra callback1 fonksiyonu çalışır. callback1 içerisinde öncelikle hata kontrolü yapılır. Hata yoksa writeFile fonksiyonu ile yazma işlemine başlanır. Sonrasında konsol ekranına “Dosya okuma işlemi bitti.” yazdırılır. Yazma işlemi bittikten sonra ise callback2 fonksiyonu çalışır. callback2 içerisinde de illk olarak hata kontrolü yapılır. Daha sonra konsol ekranına “”Dosya yazma işlemi bitti.” yazdırılır. (writeFile fonksiyonunun herhangi bir dönüşü yok.) Daha sonra programımız sonlanır. Yukarıdaki kodu şu şekildede yazabiliriz:

var fs=require("fs");

fs.readFile(process.argv[2],function (hata,dosyaIcerigi) {
    if (hata) {
        console.log("Bir hata oluştu.")
        return;
    }
    fs.writeFile(process.argv[3], dosyaIcerigi, function (hata) {
        if (hata) {
            console.log("Bir hata oluştu.")
            return;
        }
        console.log("Dosya yazma işlemi bitti.")
    });
    console.log("Dosya okuma işlemi bitti.");
});
console.log("Program devam ediyor...");

Bu şekilde kullanım çok yaygın. Ama iç içe iç içe çok fazla fonksiyon gelebilir ve kodun okunurluğu çok düşebilir. Bu tip durumlara Callback Hell yani Callback Cehennemi deniyor. Hangisi kolayınıza geliyorsa kullanabilirsiniz.

İkinci örneğimizde de birden çok asenkron fonksiyonun aynı program içerisinde kullanımı gördük. Yine callback fonksiyonların yada modül içerisindeki fonksiyonların yapısının nasıl olduğunu dökümantasyon üzerinden öğrendik. Peki yazdığımız kodlarda bize callback fonksiyonu yollanmasını nasıl sağlarız? Ve yollanan bu callback fonksiyonları nasıl kullanılır?

Bir sonraki bölümde modüler programlamayı, kendi modüllerimi yazmayı ve kendi modüllerimize callback fonksiyonları yollatıp onları kullanmayı göreceğiz.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.


Bloga e-posta ile abone ol

Bu bloga abone olmak ve e-posta ile bildirimler almak için e-posta adresinizi girin.

NodeJS Adım 3 – NodeJS I/O

Adım Adım Nodejs yazı dizimizin bir önceki yazısında nodejs uygulamarını nasıl çalıştırabileceğimizi baktık ve ilk uygulamalarımız yazmıştık.

Bu bölümde I/O (Input/Output) işlemlerine yani dosya okuma/yazma işlemlerine değineceğiz. Nodejs’in güçlü olduğu yanlardan bir taneside I/O işlemleridir. Ama I/O işlemlerine geçmeden önce bir kavramdan bahsetmem gerekiyor.

Bir önceki bölümde nodejs üzerinde global olarak kullanabileceğimiz modüller olduğundan bahsetmiştik(process modülü). Bu modüllerden bazıları nodejs çallışırken otomatik olarak yüklenirken (process modülü gibi) bazılarını da bizim kod yazarken çağırmamız gerekiyor. Çağırma işlemi şu şekilde yapılıyor “var m1=require(‘modul1’)” . modul1 modülünü m1 değişkenine atıyoruz. m1 değişkeni ile modul1 modülünün tüm içeriğine erişebiliriz. Bu modüler yapının ayrıntılarına daha sonra değineceğiz. Şimdilik bu kadarı yeterli.

Gelelim konumuza. I/O işlemleri için nodejs içerisinde fs(file system) modülü kullanılmaktadır. Dosyalarla alakalı bir çok işimizi bu modülle yapabiliriz. Bu bölümde yapacağımız ilk örnekte bir dosya okuyup içeriğini ekrana yazdıracağız. Bunun için fs modülünün readFileSync metodunu kullanacağız. Kodlarımız şu şekilde:

// fs modülünü çağırıyoruz
var fs=require("fs");

//dosyamızın yolunu verip içeriğini değişkene atıyoruz
var dosyaIcerigi=fs.readFileSync("D:\\nodejsdersleri\\ornekdosya.txt");

//dosya içeriğimizi ekrana yazdırıyoruz.
console.log(dosyaIcerigi.toString());

Bu kodlar çalıştırdığımızda çıktımız şu şekilde olacak:

3-1-nodeio1

Bir sonraki örnekte dosya ismini konsol üzerinden alacağız. Ve ekrana satır sayısını yazdıracağız. Bunun için ikinci bölümde bahsetmiş olduğumuz process modülünden faydalanacağız. Konsoldan dosya yolunu verirken yolun tamamını yazabileceğimiz gibi sadece çalışan program dosyasına göre yolunuda vere  Satır sayısını almak için ise gelen ifadeyi split metodu ile parçalayacağız. Parçalarken de parametre olarak “\n” ifadesini kullanacağız. “\n” ifadesi text dosyalarında yeni satır anlamına gelir. Yazı editörlerinde biz Enter tuşuna bastığımızda yeni bir satıra geçer ama aslında yaptığı şey arka planda yazının içerisine \n ifadesini eklemektir. Biz programımız içerisinde dosyadan okumuş olduğumuz yazıyı \n ifadelerine göre parçalarsak her bir satırı elde etmiş oluruz. Bunların sayısı da bize satır sayısını verir. Kodlarımızın anlattıklarımıza göre düzenlenmiş hali şu şekilde:

var fs=require("fs");

//Dosya yolunu konsoldan alıyoruz
var dosyaIcerigi=fs.readFileSync(process.argv[2]);

//Elde etmiş olduğumuz yazımızı satırlara ayırıyoruz
var satirlar=dosyaIcerigi.toString().split("\n");

//Satır sayısını ekrana yazdırıyoruz.
console.log(satirlar.length);

Bu kodlar sonucu çıktımız şu şekilde olacaktır:

3-2-nodeio2

Gelelim yazma işlemlerine. Yazma işlemleri içinde yine fs modülünü kullanacağız. fs modülü içerisindeki writeFileSync metodu dosya içerisine yazı yazmamızı sağlamaktadır. Şimdiki örneğimizde konsoldan hem dosya yolunu hemde içerisine ne yazacağımızı alacağız.Ufak bir not: konsol üzerindeki parametreler boşluklarla birbirinden ayrılır. Bizim dosyaya yazdıracağımız yazı boşluk içeriyor ise bu yazıyı konsola parametre olarak yollamak için tırnak içine almamız gerekiyor. Örnek kodlarımız şu şekilde olacak:

var fs=require("fs");

// Dosya ismini ve içeriğini konsoldan alıyoruz
var dosyaAdi=process.argv[2]
var yazi=process.argv[3]

//Dosyamızın içerisine yazımızı yazıyoruz
fs.writeFileSync(dosyaAdi,yazi)

Bu kodları çalıştırdığımızda sonuç şu şekilde olacaktır:

3-3-nodeio3

I/O bölümünde anlatacaklarım bu kadar. Bu bölümde bir dosyayı okumayı, içeriği ile ilgili basit işlemler yapmayı ve dosyaya yazı yazmayı öğrendik. Tabi burada basit text dosyasını okuduk ve onun üzerinde işlemler yaptık. Text dosyası dışında resim, müzik gibi yüzlerce dosya formatı var ve doğal olarak yapıları birbirinden farklı. Bunları işlemek içinde her birinin formatını bilmek gerekir. Yada onlara özel kütüphaneler/modüller kullanarak işlemek gerekir. Bunlarla alakalı bilgi almak için arama motorlarını kullanabilirsiniz.

Makalede kullanılan kodlara buradan ulaşabilirsiniz.

Bir sonraki bölümde asenkron programlamayı öğreneceğiz.

NodeJS Adım 2 – NodeJS ile ilk uygulamamız

Adım Adım Nodejs yazı diziminin bir önceki yazısında front-end / back-end kavramlarından, Javascript’in ne olduğundan ve asıl konumuz Nodejs’in ne olduğundan bahsettik.

Gelelim kod yazmaya. Nodejs ile kod yazmak için tabiki öncelikle kurulumunu yapmamız gerekiyor. Nodejs Windows, Linux, macOS işletim sistemlerinde çalışabilmektedir. Çalışma mantığı her yerde aynıdır. nodejs.org adresinden sisteminize uygun kurulum dosyasını indirebilir sisteminize kurabilirsiniz. Nodejs ile uğraşırken bol bol terminal kullanacağız. (Temel konsol komutları için buraya bakabilirsiniz.) Nodejs kurulumunu kontrol etmek için terminal ekranına node -v  komutunu girebilirsiniz. Komut sonucunda kurmuş olduğunuz nodejs sürümünün numarası gelmiş olmalı.

2-1-nodev

Kurulum aşamasından sonra bize gerekli olan bir terminal ekranı birde text editör. Bu yazı dizisi boyunca yapacağımız örnekler için benim kullanacağım editör Visual Studio Code olacak. Ama bunun dışında kullanabileceğiniz birçok editör var: Notepad++, Atom, Brackets, Vim, hatta Notepad 🙂 Bunların dışında IDE’lerde kullanabilirsiniz. Kendiniz için uygun olan veya daha önceden alışkın olduğunuz herhangi bir editor/IDE kullanabilirsiniz.

Merhaba Dünya!

Kendimiz için uygun bir klasör seçtikten sonra yeni bir dosya oluşturuyoruz. Ben D:\nodejsdersleri klasörünü kullanacağım. Bu klasör içerisine program.js adında bir dosya oluşturuyoruz. Ve ilk örneğimizi yazmaya başlıyoruz 🙂

“Daha öncede belirttiğimiz gibi Nodejs javascript’in backend’de çalışmasını sağlayan motordur. Dolayısıyla yazacağımız bütün kodlar aslında javascript olacak.”

İlk örneğimiz konsol ekranına “Merhaba Dünya!” yazdırmak olacak. Javascript kullanarak konsol ekranına herhangi bir veri yollamak için console.log() methodunu kulllanıyorduk. program.js dosyası içerisine şu kodu yazıyoruz.

console.log("Merhaba Dünya!")

Şimdi bu kodumuzu çalıştırmak için konsol ekranına geliyoruz. program.js dosyasına ilerliyoruz. (Temel konsol komutları için buraya bakabilirsiniz.) Daha sonra konsol üzerinden node program.js  komutunu veriyoruz. Herşey yolunda giderse konsol ekranına Merhaba Dünya! yazısı çıkmış olmalı. Evet ilk nodejs kodumuzu yazmış olduk.

2-2-nodehello

İkinci örnek olarak konsol ekranından parametre olarak sayılar yollayıp toplamlarını ekrana yazdıracağız.. nodejs sisteminde global olarak kullanabileceğimiz modüller var. Bunlardan bir tanesi de process modülü. process modülü ile o anki nodejs prosesi hakkında bilgi alabilir ve kontrol edebiliriz. Konsolda program çalıştırırken yollanan parametrelere de process sınıfının argv değişkeni ile ulaşabiliriz. Bu değişken bize liste halinde girilen bütün parametreleri gönderir. (Konsolda girilen parametreler birbirlerinden boşluklarla ayrılır.) Bu listenin ilk elemanı nodejs’in çalıştığı yeri, ikinci parametre program.js yani kodlarımızın çalıştığı yeri sonraki parametreler de konsol ekranından girilen parametreleri verir. Kodumuzu şu şekilde düzenlersek process.argv değişkeninin içeriğini konsola yazdırabiliriz.

console.log(process.argv)

Programımızı konsol üzerinden node program.js Nodejs Dersleri  komutu ile çalıştıralım. Ekran çıktımız şu şekilde olacaktır.

2-3-nodeargv

Gelelim toplama programına. Konsoldan aldığımız parametreleri toplatıp ekrana yazdıracağız dedik. Bunun için kodlarımız şu şekilde olmalı:

var toplam=0;

// Döngünün 2’den başladığına dikkat edin. Neden? 🙂
for (var i = 2; i < process.argv.length; i++) {
   toplam+=Number(process.argv[i])
}
console.log(toplam)

Bu kodları yazdığımızda da çıktımız şu şekilde olacaktır.

2-4-nodetoplam

Evet ilk dersimizin sonuna geldik. Bu derste nodejs’i nasıl çalıştıracağımızı, konsol ekranına nasıl yazı yazdıracağımızı ve konsoldan parametre alıp nasıl kullanabileceğimizi gördük.

Bir sonraki bölümde I/O işlemlerini inceleyeceğiz.

NodeJS Adım 1 – NodeJS Nedir?

Adım Adım Nodejs yazılarımızın ilkinde sizlere birkaç ön bilgi ile beraber nodejs’in ne olduğundan bahsedeceğim.

Frontend & Backend Kavramı

Yazdığımız uygulamalar yüksek ihtimalle çok kullanıcılı uygulamalardır. Çoklu kullanıcılı uygulamalar genelde Sunucu(Server)/İstemci(Client) mantığı ile yazılır. Sunucu yani çevrim içi çalışan, tüm verilerin tutulduğu merkezi bir sistem. İstemci yani bu merkezi sisteme bağlı çalışan merkezi sisteme veri iletimi yapan, kullanıcıyla etkileşimi sağlayan yan program.

serverside-clientside

Günümüz modern uygulamalar da bu istemci genelde tarayıcıdır(browser). Tarayıcı üzerinden kullanılan uygulamalar genelde Web Uygulaması olarak adlandırılır. Kullanıcı uygulamayı kullanmak istediğinde tarayıcıdan sunucuya bir istek gönderir, sunucu isteğe göre uygulama içerisinde kendi anlayacağı bölümleri işleyerek kullanıcıya uygun çıktıyı üretir ve tarayıcıya gönderir. Tarayıcı aldığı çıktıyı kendi içerisinde yorumlar ve kullanıcıya uygun çıktıyı üretir. Sonra kullanıcı gerektiği yerde tekrar sunucuya istek gönderir, tekrar cevap gelir… Genel olarak tarayıcı bazlı Sunucu/İstemci mimarisi basit anlatımla bu şekilde çalışır.

Burada önemli bir kısım sunucunun kendi anladığı kısımları yani sunucu tabanlı diller(C#, Java, Php, Python, Ruby…) ile yazılmış kısımları çalıştırması, tarayıcının da kendi anladığı yani tarayıcı dilleri(HTML,Css,Javascript, Dart… ) ile yazılmış kısımları çalıştırmasıdır. Sunucu tarafında çalışan koda Backend, tarayıcı üzerinde çalışan koda Frontend denir. Web uygulamarı yazarken Backend ve Frontend kodları aynı proje içerisinde kullanırız. Hangi iş mantığı hangi kısımda çalışacaksa ona göre ya Backend’de yada Frontend’de yazılır.

unofficial_javascript_logo_2-svg

Javascript

Frontend dillerinden en önemlilerinden bir tanesi Javascript’tir. Web tarayıcılarının etkin bir şekilde kullanılması için geliştirilmiştir. Kullanıcıyla etkileşimin artırılması, iş yükünün sunuculardan istemcilere taşınması, daha görsel animasyonlu web siteleri/ web uygulamaları geliştirilmesi gibi kullanım alanları vardır. 1995 yıllarında çıktı. Java diliyle isim benzerliği dışında hiçbir alakası yoktur. Javasvript standartları Ecma International firması tarafından ECMASCRIPT standartlarıyla belirlenir. Günümüzde kullanılan ECMASCRIPT standardı 5.1 sürümüdür. Ve bu standart Javascript 1.8.1 ile desteklenmektedir. Ancak şu aralar ECMASCRIPT 6 standardı üzerinde çalışmalar devam etmektedir.

Javascript prototip bazlı nesne yönelimli programlama, fonksiyonel programlama, imparatif programlama paradigmalarını destekler. Genellikle tarayıcılarda kullanılır. Google’ın Chrome tarayıcısını ve bu tarayıcı içerisindeki V8 Javascript motorunu geliştirmesiyle Javascript performansında ve gelişimde büyük yükselişler oldu. Bu Javascript motorunun gücü sayesinde daha önce yapılamayan birçok uygulama yapıldı. Ve ilk defa Javascript frontend’den çıkarak Backend tarafına geçiş yaptı ve Nodejs doğdu.

node_js_logo

NodeJS

NodeJs 2009 yılında Joyent firmasında çalışan Ryan Dahl tarafından geliştirilen Javascript Çalışma Ortamıdır.(Javascript Runtime Enviroment). Joyent firmasının desteği ve V8 motorunun gücü ile Dahl Javascript’i Backend kısmına taşıdı.

“Nodejs backend tarafında çalışan, javascript tabanlı scriptleri yorumlanabilen ve çalıştırabilen, hızlı, ölçeklenebilir  network uygulamaları geliştirmeyi sağlayan bir çalışma zamanı ortamıdır(runtime enviroment).”

Javascript’in backend’e(server side) taşınması ile developerlar tek dil bilerek hem frontend hem backend kod yazabilir hale geldiler. Frontend de kullanılan javascript kütüphanelerinden bazıları backend tarafında kullanılabilir hale geldi.

Javascript’in doğal Non Block mimarisi sayesinde backend tarafında ölçeklenebilir uygulamalar daha kolay bir şekilde yazılabilir hale geldi. Nodejs’in ölçeklenebilir uygulamalardaki başarısı sayesinde kullanıcı sayısı milyonlarla ifade edilen büyük siteler nodejs kullanmaya başladı. Üstelik bunu single thread olarak yapabiliyor.

“Ölçeklenebilirlik, bir uygulamanın aynı anda birden çok kullanıcıya takılmadan cevap verebilir halde olmasıdır.”

Peki bunu nasıl başarıyor? Nodejs olay bazlı(event-driven) Non-Blocking yapısını Event loop denilen bir mimariyle sağlıyor. (İlerde detayına gireceğiz.) Bu mimariyle tek bir thread(single thread) ile asenkron çalışabilmektedir.

Bunların dışında Nodejs’in en büyük avantajlarından bir taneside modüler yapısı ve bu yapıyı destekleyen paket yönetim sistemi: npm(nodejs package manager). Nodejs npm ve npm yi destekleyen topluluk sayesinde hemen hemen her işimizi paketler sayesinde halledebiliyoruz. Şuan npm üzerindeki paket sayısı yaklaşık 250000 civarında. E okadar paket içerisinde mutlaka işimize yarayan bir paket vardır :). Tabi bu kadar paket Nodejs’in arkasında çok büyük bir topluluğun olduğunuda gösteriyor. Bu kadar büyük bir topluluğun içerisinde sorunları çözmekte kolay.

Bir sonraki yazımızda örnek kodlara başlayacağız ve ilk uygulamalarımız yazacağız.