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

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

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

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

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

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

Ö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.

Exit mobile version