From 48a4d8367096fd2ceaad02017834c984d7246333 Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 20:25:15 +0300 Subject: [PATCH 1/8] update 02.7.md for turkish --- tr/02.7.md | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 tr/02.7.md diff --git a/tr/02.7.md b/tr/02.7.md new file mode 100644 index 000000000..3ed9c757f --- /dev/null +++ b/tr/02.7.md @@ -0,0 +1,250 @@ +# Concurrency + +Go'nun 21. yüzyılın C'si olduğu söyleniyor. Bence bunun iki nedeni var. Birincisi, Go basit bir dil. İkincisi, eşzamanlılık günümüz dünyasında çok popüler bir konu ve Go bu özelliği dil seviyesinde destekliyor. + +## goroutine + +goroutine'ler ve eşzamanlılık Go'nun çekirdek tasarımında yer almaktadır. Thread'lere benzerler ancak farklı çalışırlar. Go ayrıca goroutinlerinizde bellek paylaşımı için tam destek sağlar. Bir goroutine genellikle 4~5 KB yığın bellek kullanır. Bu nedenle, tek bir bilgisayarda binlerce goroutine çalıştırmak zor değildir. Bir goroutine, sistem iş parçacıklarından daha hafif, daha verimli ve daha kullanışlıdır. + +goroutine'ler Go'da çalışma zamanında iş parçacığı yöneticisi üzerinde çalışır. Temel düzeyde bir fonksiyon olan yeni bir goroutine oluşturmak için `go` anahtar sözcüğünü kullanırız ( ***main() bir goroutine'dir*** ). +```Go +go hello(a, b, c) +``` +Bir örnek görelim. +```Go +package main + +import ( + "fmt" + "runtime" +) + +func say(s string) { + for i := 0; i < 5; i++ { + runtime.Gosched() + fmt.Println(s) + } +} + +func main() { + go say("dünya") // yeni bir goroutine oluşturun + say("merhaba") // mevcut goroutine +} + +``` +Çıktı: +``` + merhaba + dünya + merhaba + dünya + merhaba + dünya + merhaba + dünya + merhaba +``` +Go'da `go` anahtar kelimesini kullanarak eşzamanlılığı kullanmanın çok kolay olduğunu görüyoruz. Yukarıdaki örnekte, bu iki goroutin bir miktar bellek paylaşmaktadır, ancak tasarım tarifini takip etmemiz daha iyi olacaktır: İletişim kurmak için paylaşılan verileri kullanmayın, veri paylaşmak için iletişimi kullanın. + +`runtime.Gosched()`, CPU'nun diğer goroutinleri yürütmesine ve bir noktada geri gelmesine izin vermek anlamına gelir. + +Go 1.5'te, çalışma zamanı artık `GOMAXPROCS` tarafından tanımlanan aynı anda çalışacak varsayılan iş parçacığı sayısını CPU'daki mevcut çekirdek sayısına göre ayarlamaktadır. + +Go 1.5'ten önce, zamanlayıcı tüm goroutinleri çalıştırmak için yalnızca bir iş parçacığı kullanır, bu da yalnızca eşzamanlılığı uyguladığı anlamına gelir. Paralel işlemeden yararlanmak için daha fazla CPU çekirdeği kullanmak istiyorsanız, kullanmak istediğiniz çekirdek sayısını ayarlamak için runtime.`GOMAXPROCS(n)` komutunu çağırmanız gerekir. Eğer `n<1` ise, hiçbir şey değişmez. + +## channels + +goroutinler aynı bellek adres alanında çalışır, bu nedenle paylaşılan belleğe erişmek istediğinizde senkronizasyonu sürdürmeniz gerekir. Farklı goroutinler arasında nasıl iletişim kurarsınız? Go, `channel` adı verilen çok iyi bir iletişim mekanizması kullanır. Bir `channel` Unix kabuklarındaki iki yönlü boru hattı gibidir: veri göndermek veya almak için `channel` kullanın. Kanallarda kullanılabilecek tek veri tipi `channel` tipi ve `chan` anahtar kelimesidir. Yeni bir `kanal` oluşturmak için `make` kullanmanız gerektiğini unutmayın. +```Go +ci := make(chan int) +cs := make(chan string) +cf := make(chan interface{}) +``` +kanalı veri göndermek veya almak için `<-` operatörünü kullanır. +```Go +ch <- v // v'yi kanal ch'ye gönderin. +v := <-ch // ch'den veri alır ve v'ye atar +``` +Daha fazla örnek görelim. +```Go +package main + +import "fmt" + +func sum(a []int, c chan int) { + total := 0 + for _, v := range a { + total += v + } + c <- total // toplamı c'ye gönder +} + +func main() { + a := []int{7, 2, 8, -9, 4, 0} + + c := make(chan int) + go sum(a[:len(a)/2], c) + go sum(a[len(a)/2:], c) + x, y := <-c, <-c // toplamı c'den al + + fmt.Println(x, y, x+y) +} + +``` +Kanallarda veri gönderme ve alma varsayılan olarak bloklanır, bu nedenle senkron goroutinleri kullanmak çok daha kolaydır. Bloktan kastım, bir goroutinin boş bir kanaldan veri alırken, yani (`value := <-ch`), diğer goroutinler bu kanala veri gönderene kadar devam etmeyeceğidir. Öte yandan, goroutine bir kanala gönderdiği veri, yani (`ch<-5`), alınana kadar devam etmeyecektir. + +## Buffered channels + +Yukarıda tamponlanmamış kanalları tanıttım. Go aynı zamanda tek bir elemandan daha fazlasını saklayabilen tamponlu kanallara da sahiptir. Örneğin, `ch := make(chan bool, 4)`, burada 4 boolean elemanı saklayabilen bir kanal oluşturuyoruz. Yani bu kanalın içine bloklama olmadan 4 eleman gönderebiliyoruz, ancak beşinci bir eleman göndermeye çalıştığınızda ve hiçbir goroutine bunu almadığında goroutine bloklanacaktır. +```Go +ch := make(chan type, n) + +n == 0 ! non-buffer(block) +n > 0 ! buffer(non-block until n elements in the channel) +``` +Aşağıdaki kodu bilgisayarınızda deneyebilir ve bazı değerleri değiştirebilirsiniz. +```Go +package main + +import "fmt" + +func main() { + c := make(chan int, 2) // 2'yi 1 olarak değiştirirseniz çalışma zamanı hatası olur, ancak 3 iyidir + c <- 1 + c <- 2 + fmt.Println(<-c) + fmt.Println(<-c) +} + +``` +## Range and Close + +Dilim ve eşlemede olduğu gibi tampon kanallar üzerinde çalışmak için aralığı kullanabiliriz. +```Go +package main + +import ( + "fmt" +) + +func fibonacci(n int, c chan int) { + x, y := 1, 1 + for i := 0; i < n; i++ { + c <- x + x, y = y, x+y + } + close(c) +} + +func main() { + c := make(chan int, 10) + go fibonacci(cap(c), c) + for i := range c { + fmt.Println(i) + } +} + +``` +`for i := range c` kanal kapatılana kadar kanaldan veri okumayı durdurmayacaktır. Yukarıdaki örnekte kanalı kapatmak için `close` anahtar sözcüğünü kullanıyoruz. Kapalı bir kanalda veri göndermek veya almak imkansızdır; bir kanalın kapalı olup olmadığını test etmek için `v, ok := <-ch` kullanabilirsiniz. Eğer `ok` false döndürürse, bu kanalda veri olmadığı ve kanalın kapalı olduğu anlamına gelir. + +Kanalları her zaman tüketicilerde değil üreticilerde kapatmayı unutmayın, aksi takdirde panik durumuna geçmek çok kolaydır. + +Hatırlamanız gereken bir başka şey de kanalların dosyalar gibi olmadığıdır. Kanalın tamamen yararsız olduğundan emin değilseniz veya aralık döngülerinden çıkmak istemiyorsanız, bunları sık sık kapatmanız gerekmez. + +## Select + +Yukarıdaki örneklerde sadece bir kanal kullandık, ancak birden fazla kanalla nasıl başa çıkabiliriz? Go, birçok kanalı dinlemek için `select` adlı bir anahtar kelimeye sahiptir. + +`select` varsayılan olarak engelleyicidir ve yalnızca kanallardan birinde gönderilecek veya alınacak veri olduğunda çalışmaya devam eder. Aynı anda birden fazla kanal kullanıma hazırsa, select hangisinin çalıştırılacağını rastgele seçer. +```Go +package main + +import "fmt" + +func fibonacci(c, quit chan int) { + x, y := 1, 1 + for { + select { + case c <- x: + x, y = y, x+y + case <-quit: + fmt.Println("quit") + return + } + } +} + +func main() { + c := make(chan int) + quit := make(chan int) + go func() { + for i := 0; i < 10; i++ { + fmt.Println(<-c) + } + quit <- 0 + }() + fibonacci(c, quit) +} + +``` +`select`in de tıpkı `switch` gibi bir `default` durumu vardır. Tüm kanallar kullanıma hazır olmadığında, varsayılan durumu çalıştırır (artık kanalı beklemez). +```Go +select { +case i := <-c: +// use i +default: +// executes here when c is blocked +} +``` +## Timeout + +Bazen bir goroutine bloke olur. Tüm programın bloke olmasını önlemek için bunu nasıl önleyebiliriz? Çok basit, select içinde bir zaman aşımı ayarlayabiliriz. +```Go +func main() { + c := make(chan int) + o := make(chan bool) + go func() { + for { + select { + case v := <-c: + println(v) + case <-time.After(5 * time.Second): + println("timeout") + o <- true + break + } + } + }() + <-o +} + +``` +## Runtime goroutine + +`runtime` paketi goroutine'lerle çalışmak için bazı fonksiyonlara sahiptir. + +- `runtime.Goexit()` + + Geçerli goroutine'den çıkar, ancak ertelenen işlevler her zamanki gibi yürütülür. + +- `runtime.Gosched()` + + Zamanlayıcının diğer goroutinleri yürütmesine ve bir noktada geri gelmesine izin verir. + +- `runtime.NumCPU() int` + + CPU çekirdeği sayısını döndürür + +- `runtime.NumGoroutine() int` + + Goroutin sayısını döndürür + +- `runtime.GOMAXPROCS(n int) int` + + Kaç CPU çekirdeği kullanmak istediğinizi ayarlar + +## Links + +- [Directory](preface.md) +- Önceki bölüm: [interface](02.6.md) +- Sonraki bölüm: [Summary](02.8.md) From 9de95b09d70dc91f88e2366b448d12fbd7f9690f Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 20:34:36 +0300 Subject: [PATCH 2/8] update 02.8.md for Turkish --- tr/02.8.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tr/02.8.md diff --git a/tr/02.8.md b/tr/02.8.md new file mode 100644 index 000000000..bc907e8cb --- /dev/null +++ b/tr/02.8.md @@ -0,0 +1,32 @@ +# 2.8 Özet + +Bu bölümde temel olarak 25 Go anahtar kelimesini tanıttık. Şimdi bunların ne olduklarını ve ne işe yaradıklarını gözden geçirelim. +```Go + break default func interface select + case defer go map struct + chan else goto package switch + const fallthrough if range type + continue for import return var +``` +- `var` ve `const` değişkenleri ve sabitleri tanımlamak için kullanılır. +- `package` ve `import` paket kullanımı içindir. +- `func` fonksiyonları ve metotları tanımlamak için kullanılır. +- `return` fonksiyonlarda veya metotlarda değer döndürmek için kullanılır. +- `defer`, defer fonksiyonlarını tanımlamak için kullanılır. +- `go` yeni bir goroutine başlatmak için kullanılır. +- `select` iletişim için birden fazla kanal arasında geçiş yapmak için kullanılır. +- `interface` arayüzleri tanımlamak için kullanılır. +- `struct` özel özelleştirilmiş türleri tanımlamak için kullanılır. +- `break`, `case`, `continue`, `for`, `fallthrough`, `else`, `if`, `switch`, `goto` ve `default` bölüm 2.3'te tanıtılmıştır. +- `chan` goroutinler arasındaki iletişim için kullanılan kanal türüdür. +- `type` özelleştirilmiş türleri tanımlamak için kullanılır. +- `map` diğer dillerdeki hash tablolarına benzeyen map tanımlamak için kullanılır. +- `range`, `slice`, `map` ve `channel`'dan veri okumak için kullanılır. + +Bu 25 anahtar kelimeyi nasıl kullanacağınızı anladıysanız, Go hakkında çok şey öğrenmişsiniz demektir. + +## Bağlantılar + +- [Directory](preface.md) +- Önceki bölüm: [Eşzamanlılık](02.7.md) +- Sonraki bölüm: [Web vakfı](03.0.md) From 284c9d1b8e25399a06e7b0b08a26267c78569d53 Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 20:42:14 +0300 Subject: [PATCH 3/8] update next and previous title --- tr/02.7.md | 4 ++-- tr/02.8.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tr/02.7.md b/tr/02.7.md index 3ed9c757f..36cebb066 100644 --- a/tr/02.7.md +++ b/tr/02.7.md @@ -246,5 +246,5 @@ func main() { ## Links - [Directory](preface.md) -- Önceki bölüm: [interface](02.6.md) -- Sonraki bölüm: [Summary](02.8.md) +- Önceki bölüm: [Interface - Arayüz](02.6.md) +- Sonraki bölüm: [Summary - Özet](02.8.md) diff --git a/tr/02.8.md b/tr/02.8.md index bc907e8cb..b831220ba 100644 --- a/tr/02.8.md +++ b/tr/02.8.md @@ -25,7 +25,7 @@ Bu bölümde temel olarak 25 Go anahtar kelimesini tanıttık. Şimdi bunların Bu 25 anahtar kelimeyi nasıl kullanacağınızı anladıysanız, Go hakkında çok şey öğrenmişsiniz demektir. -## Bağlantılar +## Links - [Directory](preface.md) - Önceki bölüm: [Eşzamanlılık](02.7.md) From c99eff076bc5ef8af7a85a0ed3acac2444772c1f Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 20:47:19 +0300 Subject: [PATCH 4/8] update title --- tr/02.7.md | 20 ++++++++++---------- tr/02.8.md | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tr/02.7.md b/tr/02.7.md index 36cebb066..e205a35b3 100644 --- a/tr/02.7.md +++ b/tr/02.7.md @@ -1,8 +1,8 @@ -# Concurrency +# Concurrency - Eşzamanlılık Go'nun 21. yüzyılın C'si olduğu söyleniyor. Bence bunun iki nedeni var. Birincisi, Go basit bir dil. İkincisi, eşzamanlılık günümüz dünyasında çok popüler bir konu ve Go bu özelliği dil seviyesinde destekliyor. -## goroutine +## Goroutine goroutine'ler ve eşzamanlılık Go'nun çekirdek tasarımında yer almaktadır. Thread'lere benzerler ancak farklı çalışırlar. Go ayrıca goroutinlerinizde bellek paylaşımı için tam destek sağlar. Bir goroutine genellikle 4~5 KB yığın bellek kullanır. Bu nedenle, tek bir bilgisayarda binlerce goroutine çalıştırmak zor değildir. Bir goroutine, sistem iş parçacıklarından daha hafif, daha verimli ve daha kullanışlıdır. @@ -52,7 +52,7 @@ Go 1.5'te, çalışma zamanı artık `GOMAXPROCS` tarafından tanımlanan aynı Go 1.5'ten önce, zamanlayıcı tüm goroutinleri çalıştırmak için yalnızca bir iş parçacığı kullanır, bu da yalnızca eşzamanlılığı uyguladığı anlamına gelir. Paralel işlemeden yararlanmak için daha fazla CPU çekirdeği kullanmak istiyorsanız, kullanmak istediğiniz çekirdek sayısını ayarlamak için runtime.`GOMAXPROCS(n)` komutunu çağırmanız gerekir. Eğer `n<1` ise, hiçbir şey değişmez. -## channels +## Channels - Kanallar goroutinler aynı bellek adres alanında çalışır, bu nedenle paylaşılan belleğe erişmek istediğinizde senkronizasyonu sürdürmeniz gerekir. Farklı goroutinler arasında nasıl iletişim kurarsınız? Go, `channel` adı verilen çok iyi bir iletişim mekanizması kullanır. Bir `channel` Unix kabuklarındaki iki yönlü boru hattı gibidir: veri göndermek veya almak için `channel` kullanın. Kanallarda kullanılabilecek tek veri tipi `channel` tipi ve `chan` anahtar kelimesidir. Yeni bir `kanal` oluşturmak için `make` kullanmanız gerektiğini unutmayın. ```Go @@ -93,7 +93,7 @@ func main() { ``` Kanallarda veri gönderme ve alma varsayılan olarak bloklanır, bu nedenle senkron goroutinleri kullanmak çok daha kolaydır. Bloktan kastım, bir goroutinin boş bir kanaldan veri alırken, yani (`value := <-ch`), diğer goroutinler bu kanala veri gönderene kadar devam etmeyeceğidir. Öte yandan, goroutine bir kanala gönderdiği veri, yani (`ch<-5`), alınana kadar devam etmeyecektir. -## Buffered channels +## Buffered Channels - Tamponlanmış Kanallar Yukarıda tamponlanmamış kanalları tanıttım. Go aynı zamanda tek bir elemandan daha fazlasını saklayabilen tamponlu kanallara da sahiptir. Örneğin, `ch := make(chan bool, 4)`, burada 4 boolean elemanı saklayabilen bir kanal oluşturuyoruz. Yani bu kanalın içine bloklama olmadan 4 eleman gönderebiliyoruz, ancak beşinci bir eleman göndermeye çalıştığınızda ve hiçbir goroutine bunu almadığında goroutine bloklanacaktır. ```Go @@ -117,7 +117,7 @@ func main() { } ``` -## Range and Close +## Range and Close - Aralık ve Kapanış Dilim ve eşlemede olduğu gibi tampon kanallar üzerinde çalışmak için aralığı kullanabiliriz. ```Go @@ -151,7 +151,7 @@ Kanalları her zaman tüketicilerde değil üreticilerde kapatmayı unutmayın, Hatırlamanız gereken bir başka şey de kanalların dosyalar gibi olmadığıdır. Kanalın tamamen yararsız olduğundan emin değilseniz veya aralık döngülerinden çıkmak istemiyorsanız, bunları sık sık kapatmanız gerekmez. -## Select +## Select - Seçim Yukarıdaki örneklerde sadece bir kanal kullandık, ancak birden fazla kanalla nasıl başa çıkabiliriz? Go, birçok kanalı dinlemek için `select` adlı bir anahtar kelimeye sahiptir. @@ -196,7 +196,7 @@ default: // executes here when c is blocked } ``` -## Timeout +## Timeout - Zaman Aşımı Bazen bir goroutine bloke olur. Tüm programın bloke olmasını önlemek için bunu nasıl önleyebiliriz? Çok basit, select içinde bir zaman aşımı ayarlayabiliriz. ```Go @@ -219,7 +219,7 @@ func main() { } ``` -## Runtime goroutine +## Runtime Goroutine - Çalışma Zamanı `runtime` paketi goroutine'lerle çalışmak için bazı fonksiyonlara sahiptir. @@ -243,8 +243,8 @@ func main() { Kaç CPU çekirdeği kullanmak istediğinizi ayarlar -## Links +## Links - Linkler - [Directory](preface.md) -- Önceki bölüm: [Interface - Arayüz](02.6.md) +- Önceki bölüm: [Interface - Arabirim](02.6.md) - Sonraki bölüm: [Summary - Özet](02.8.md) diff --git a/tr/02.8.md b/tr/02.8.md index b831220ba..ab37e2981 100644 --- a/tr/02.8.md +++ b/tr/02.8.md @@ -25,7 +25,7 @@ Bu bölümde temel olarak 25 Go anahtar kelimesini tanıttık. Şimdi bunların Bu 25 anahtar kelimeyi nasıl kullanacağınızı anladıysanız, Go hakkında çok şey öğrenmişsiniz demektir. -## Links +## Linkler - [Directory](preface.md) - Önceki bölüm: [Eşzamanlılık](02.7.md) From d029a8a58dc911d9d7d3b8c44cb1c2bc55ee9f23 Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 21:17:09 +0300 Subject: [PATCH 5/8] update title --- tr/02.7.md | 4 ++-- tr/02.8.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tr/02.7.md b/tr/02.7.md index e205a35b3..5b2eac398 100644 --- a/tr/02.7.md +++ b/tr/02.7.md @@ -243,8 +243,8 @@ func main() { Kaç CPU çekirdeği kullanmak istediğinizi ayarlar -## Links - Linkler +## Linkler -- [Directory](preface.md) +- [İçerik](preface.md) - Önceki bölüm: [Interface - Arabirim](02.6.md) - Sonraki bölüm: [Summary - Özet](02.8.md) diff --git a/tr/02.8.md b/tr/02.8.md index ab37e2981..7411129c3 100644 --- a/tr/02.8.md +++ b/tr/02.8.md @@ -27,6 +27,6 @@ Bu 25 anahtar kelimeyi nasıl kullanacağınızı anladıysanız, Go hakkında ## Linkler -- [Directory](preface.md) +- [İçerik](preface.md) - Önceki bölüm: [Eşzamanlılık](02.7.md) - Sonraki bölüm: [Web vakfı](03.0.md) From 223aefaad688807c9317ea8b9848d25af8d238ed Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 21:50:57 +0300 Subject: [PATCH 6/8] update next section --- tr/02.8.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tr/02.8.md b/tr/02.8.md index 7411129c3..c31e87eeb 100644 --- a/tr/02.8.md +++ b/tr/02.8.md @@ -29,4 +29,4 @@ Bu 25 anahtar kelimeyi nasıl kullanacağınızı anladıysanız, Go hakkında - [İçerik](preface.md) - Önceki bölüm: [Eşzamanlılık](02.7.md) -- Sonraki bölüm: [Web vakfı](03.0.md) +- Sonraki bölüm: [Web Temel Kuruluşu](03.0.md) From 432954f55d0afd45a2179a5e6c2b7ce938d7d0e2 Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 21:55:47 +0300 Subject: [PATCH 7/8] update 03.* for Turkish --- tr/03.0.md | 9 +++ tr/03.1.md | 155 +++++++++++++++++++++++++++++++++++++++ tr/03.2.md | 67 +++++++++++++++++ tr/03.3.md | 86 ++++++++++++++++++++++ tr/03.4.md | 207 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tr/03.5.md | 11 +++ 6 files changed, 535 insertions(+) create mode 100644 tr/03.0.md create mode 100644 tr/03.1.md create mode 100644 tr/03.2.md create mode 100644 tr/03.3.md create mode 100644 tr/03.4.md create mode 100644 tr/03.5.md diff --git a/tr/03.0.md b/tr/03.0.md new file mode 100644 index 000000000..b04e61651 --- /dev/null +++ b/tr/03.0.md @@ -0,0 +1,9 @@ +# 3 Web Temel Kuruluşu + +Bu kitabı okumanızın nedeni, Go'da web uygulamaları oluşturmayı öğrenmek istemenizdir. Daha önce de söylediğim gibi, Go `http` gibi birçok güçlü paket sağlar. Bu paketler web uygulamaları oluşturmaya çalışırken size çok yardımcı olabilir. İlerleyen bölümlerde size bilmeniz gereken her şeyi öğreteceğim ve bu bölümde web ile ilgili bazı kavramlardan ve web uygulamalarının Go'da nasıl çalıştırılacağından bahsedeceğiz. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Bölüm 2 Özet](02.8.md) +- Sonraki bölüm: [Web Çalışma Prensipleri](03.1.md) diff --git a/tr/03.1.md b/tr/03.1.md new file mode 100644 index 000000000..e707879d2 --- /dev/null +++ b/tr/03.1.md @@ -0,0 +1,155 @@ +# Web Çalışma Prensipleri + +Tarayıcınızı her açtığınızda, bazı URL'ler yazıp enter tuşuna bastığınızda, ekranınızda güzel web sayfalarının belirdiğini göreceksiniz. Peki bu basit eylemlerin arkasında neler olduğunu biliyor musunuz? + +Normalde tarayıcınız bir istemcidir. Siz bir URL yazdıktan sonra, URL'nin ana bilgisayar kısmını alır ve ana bilgisayarın IP adresini almak için bir Alan Adı Sunucusuna (DNS) gönderir. Daha sonra IP adresine bağlanır ve bir TCP bağlantısı kurulmasını ister. Tarayıcı bağlantı üzerinden HTTP istekleri gönderir. Sunucu bunları ele alır ve web sayfasını oluşturan içeriği içeren HTTP yanıtlarıyla yanıt verir. Son olarak, tarayıcı web sayfasının gövdesini oluşturur ve sunucuyla bağlantısını keser. + +![](images/3.1.web2.png?raw=true) + +Şekil 3.1 Bir web sitesini ziyaret eden kullanıcıların süreçleri + +HTTP sunucusu olarak da bilinen bir web sunucusu, istemcilerle iletişim kurmak için HTTP protokolünü kullanır. Tüm web tarayıcıları istemci olarak kabul edilebilir. + +Web'in çalışma prensiplerini aşağıdaki adımlara ayırabiliriz: + +- İstemci sunucuya bağlanmak için TCP/IP protokolünü kullanır. +- İstemci sunucuya HTTP istek paketleri gönderir. +- Sunucu HTTP yanıt paketlerini istemciye döndürür. İstenen kaynaklar dinamik komut dosyaları içeriyorsa, sunucu önce komut dosyası motorunu çağırır. +- İstemci sunucu bağlantısını keser, HTML oluşturmaya başlar. + +Bu, HTTP işlerinin basit bir iş akışıdır - sunucunun istemcilere veri gönderdikten sonra bağlantılarını kapattığına ve ardından bir sonraki isteği beklediğine dikkat edin. + +## URL ve DNS çözünürlüğü + +Web sayfalarına erişmek için her zaman URL'leri kullanırız, ancak URL'lerin nasıl çalıştığını biliyor musunuz? + +URL'nin tam adı Uniform Resource Locator'dır. İnternet üzerindeki kaynakları tanımlamak için kullanılır ve temel şekli aşağıdaki gibidir. + + scheme://host[:port#]/path/.../[?query-string][#anchor] + scheme temel protokolü atama (HTTP, HTTPS, FTP gibi) + host HTTP sunucusunun IP veya alan adı + port# varsayılan bağlantı noktası 80'dir ve bu durumda atlanabilir. + Başka bağlantı noktaları kullanmak istiyorsanız, hangi bağlantı noktasını kullanacağınızı belirtmeniz gerekir. Örneğin, + http://www.cnblogs.com:8080/ + path kaynaklar yolu + query-string veriler sunucuya gönderilir + anchor çapa + +DNS, Alan Adı Sisteminin kısaltmasıdır. Bilgisayar ağı hizmetleri için adlandırma sistemidir ve tıpkı bir çevirmen gibi alan adlarını gerçek IP adreslerine dönüştürür. + +![](images/3.1.dns_hierachy.png?raw=true) + +Şekil 3.2 DNS çalışma prensipleri + +Çalışma prensibi hakkında daha fazla bilgi edinmek için aşağıdaki detaylı DNS çözümleme sürecini görelim. + +1. Tarayıcıya `www.qq.com` alan adını yazdıktan sonra, işletim sistemi bu alan adı için hosts dosyalarında herhangi bir eşleme ilişkisi olup olmadığını kontrol edecektir. Eğer varsa, alan adı çözümlemesi tamamlanmıştır. +2. Ana bilgisayar dosyalarında herhangi bir eşleme ilişkisi yoksa, işletim sistemi DNS'de herhangi bir önbellek olup olmadığını kontrol eder. Eğer varsa, alan adı çözümlemesi tamamlanmıştır. +3. Hem ana bilgisayar hem de DNS önbelleğinde herhangi bir eşleme ilişkisi yoksa, işletim sistemi TCP/IP ayarlarınızdaki ilk DNS çözümleme sunucusunu bulur; bu da muhtemelen yerel DNS sunucunuzdur. Yerel DNS sunucusu sorguyu aldığında, sorgulamak istediğiniz etki alanı adı bölgesel kaynaklarının yerel yapılandırmasında bulunuyorsa, sonuçları istemciye döndürür. Bu DNS çözümlemesi yetkilidir. +4. Yerel DNS sunucusu alan adını içermiyorsa ancak önbellekte bir eşleme ilişkisi varsa, yerel DNS sunucusu bu sonucu istemciye geri verir. Bu DNS çözümlemesi yetkili değildir. +5. Yerel DNS sunucusu, bölgesel kaynakların yapılandırılması veya önbellek nedeniyle bu alan adını çözümleyemezse, yerel DNS sunucusunun ayarlarına bağlı olan bir sonraki adıma geçer. + -Yerel DNS sunucusu yönlendirmeyi etkinleştirmezse, isteği kök DNS sunucusuna yönlendirir, ardından alan adını (bu durumda `.com`) bilen bir üst düzey DNS sunucusunun IP adresini döndürür. İlk üst düzey DNS sunucusu alan adını tanımazsa, alan adını tanıyan bir sunucuya ulaşana kadar isteği bir sonraki üst düzey DNS sunucusuna yönlendirir. Daha sonra en üst düzey DNS sunucusu bu bir üst düzey DNS sunucusundan `www.qq.com` adresine karşılık gelen IP adresini ister. + -Yerel DNS sunucusunda yönlendirme etkinse, isteği bir üst düzey DNS sunucusuna gönderir. Üst düzey DNS sunucusu da alan adını tanımazsa, istek sonunda alan adını tanıyan bir DNS sunucusuna ulaşana kadar daha üst düzeylere yönlendirilmeye devam eder. + +Yerel DNS sunucusu yönlendirmeyi etkinleştirse de etkinleştirmese de, alan adının IP adresi her zaman yerel DNS sunucusuna döner ve yerel DNS sunucusu bunu istemciye geri gönderir. + +![](images/3.1.dns_inquery.png?raw=true) + +Şekil 3.3 DNS çözümleme iş akışı + +`Recursive query process - Yinelemeli sorgu süreci` basitçe sorgulayıcıların süreç içinde değiştiği anlamına gelir. Yinelemeli sorgu süreçlerinde sorgulayıcılar değişmez. + +Artık istemcilerin sonunda IP adresleri aldığını biliyoruz, bu nedenle tarayıcılar sunucularla IP adresleri üzerinden iletişim kuruyor. + +## HTTP protokolü + +HTTP protokolü web hizmetlerinin temel bir parçasıdır. Web'in nasıl çalıştığını anlamadan önce HTTP protokolünün ne olduğunu bilmek önemlidir. + +HTTP, tarayıcı ve web sunucusu arasındaki iletişimi kolaylaştırmak için kullanılan protokoldür. TCP protokolüne dayanır ve genellikle web sunucusu tarafında 80 numaralı bağlantı noktasını kullanır. İstek-yanıt modelini kullanan bir protokoldür -istemciler istek gönderir ve sunucular yanıt verir. HTTP protokolüne göre, istemciler her zaman yeni bağlantılar kurar ve sunuculara HTTP istekleri gönderir. Sunucular istemcilere proaktif olarak bağlanamaz veya geri arama bağlantıları kuramaz. Bir istemci ile sunucu arasındaki bağlantı her iki tarafça da kapatılabilir. Örneğin, indirme isteğinizi ve HTTP bağlantınızı iptal edebilirsiniz ve tarayıcınız siz indirmeyi bitirmeden önce sunucuyla bağlantıyı kesecektir. + +HTTP protokolü durumsuzdur, yani her ikisi de aynı istemciden gelse bile sunucunun iki bağlantı arasındaki ilişki hakkında hiçbir fikri yoktur. Bu sorunu çözmek için, web uygulamaları bağlantıların durumunu korumak için çerezler kullanır. + +HTTP protokolü TCP protokolüne dayandığından, tüm TCP saldırıları sunucunuzdaki HTTP iletişimini etkileyecektir. Bu tür saldırılara örnek olarak SYN flooding, DoS ve DDoS saldırıları verilebilir. + +### HTTP istek paketi (tarayıcı bilgileri) + +İstek paketlerinin tümü üç bölümden oluşur: istek satırı, istek başlığı ve gövde. Başlık ve gövde arasında bir boş satır vardır. + + GET /domains/example/ HTTP/1.1 // istek satırı: istek yöntemi, URL, protokol ve sürümü + Host:www.iana.org // alan adı + User-Agent:Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4 // tarayıcı bilgileri + Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 // istemcilerin kabul edebileceği mime + Accept-Encoding:gzip,deflate,sdch // akış sıkıştırma + Accept-Charset:UTF-8,*;q=0.5 // istemci tarafında karakter kümesi + // boş satır + // gövde, istek kaynağı argümanları (örneğin, POST'taki argümanlar) + +Aşağıdaki istek bilgilerini almak için fiddler kullanıyoruz. + +![](images/3.1.http.png?raw=true) + +Şekil 3.4 Fiddler tarafından yakalanan bir GET isteğinin bilgileri + +![](images/3.1.httpPOST.png?raw=true) + +Şekil 3.5 Fiddler tarafından yakalanan bir POST isteğinin bilgileri + +**GET'in, POST'un aksine bir istek gövdesine sahip olmadığını görebiliriz.** + +HTTP'de sunucularla iletişim kurmak için kullanabileceğiniz birçok yöntem vardır; GET, POST, PUT ve DELETE genellikle kullandığımız 4 temel yöntemdir. Bir URL ağ üzerindeki bir kaynağı temsil eder, dolayısıyla bu 4 yöntem bu kaynaklar üzerinde işlem yapabilecek sorgulama, değiştirme, ekleme ve silme işlemlerini tanımlar. GET ve POST HTTP'de çok yaygın olarak kullanılır. GET, URL ve parametreleri ayırmak için `?` ve argümanlar arasında `&` kullanarak sorgu parametrelerini URL'ye ekleyebilir, örneğin `EditPosts.aspx?name=test1&id=123456`. URL, tarayıcı aracılığıyla bir uzunluk sınırlaması uyguladığından POST, verileri istek gövdesine koyar. Böylece POST, GET'e göre çok daha fazla veri gönderebilir. Ayrıca, kullanıcı adlarını ve parolaları gönderdiğimizde, bu tür bilgilerin URL'de görünmesini istemeyiz, bu nedenle bunları görünmez tutmak için POST kullanırız. + +### HTTP yanıt paketi (sunucu bilgileri) + +Yanıt paketlerinde hangi bilgilerin yer aldığını görelim. + + HTTP/1.1 200 OK // durum satırı + Server: nginx/1.0.8 // web sunucusu yazılımı ve sunucu makinesindeki sürümü + Date:Date: Tue, 30 Oct 2012 04:14:25 GMT // yanıt verilen zaman + Content-Type: text/html // yanıtlanan veri türü + Transfer-Encoding: chunked // verilerin parçalar halinde gönderildiği anlamına gelir + Connection: keep-alive // bağlantıyı sürdür + Content-Length: 90 // gövde uzunluğu + // boş satır + max { + tempDelay = max + } + log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay) + time.Sleep(tempDelay) + continue + } + return e + } + tempDelay = 0 + c, err := srv.newConn(rw) + if err != nil { + continue + } + go c.serve() + } +} +``` + +Bir portu dinlemeye başladıktan sonra istemci isteklerini nasıl kabul ederiz? Kaynak kodda, istemci isteklerini işlemek için `srv.Serve(net.Listener)` fonksiyonunun çağrıldığını görebiliriz. Fonksiyonun gövdesinde bir `for{}` vardır. Bir isteği kabul eder, yeni bir bağlantı oluşturur ve ardından yeni bir goroutine başlatarak istek verilerini `go c.serve()` goroutine'ine aktarır. Go yüksek eşzamanlılığı bu şekilde destekler ve her goroutine bağımsızdır. + +İstekleri işlemek için belirli fonksiyonları nasıl kullanırız? `conn` ilk önce `c.ReadRequest()` isteğini ayrıştırır, sonra ilgili işleyiciyi alır: `handler := sh.srv.Handler`, `ListenAndServe` fonksiyonunu çağırdığımızda verdiğimiz ikinci argümandır. Biz `nil` değerini verdiğimiz için, Go varsayılan işleyicisi `handler = DefaultServeMux` değerini kullanır. Peki `DefaultServeMux` burada ne yapıyor? Belirli URL'ler için işleyici fonksiyonlarını çağırabilen yönlendirici değişkeni. Bunu ayarladık mı? Evet, ayarladık. Bunu `http.HandleFunc("/", sayhelloName)` kullandığımız ilk satırda yaptık. Bu fonksiyonu "/" yolu için yönlendirici kuralını kaydetmek için kullanıyoruz. URL `/` olduğunda, yönlendirici `sayhelloName` fonksiyonunu çağırır. DefaultServeMux, farklı yollar için işleyici işlevleri almak üzere ServerHTTP'yi çağırır ve bu özel durumda `sayhelloName` işlevini çağırır. Son olarak, sunucu verileri yazar ve istemcilere yanıt verir. + +Detaylı iş akışı: + +![](images/3.3.illustrator.png?raw=true) + +Şekil 3.10 Bir HTTP isteğinin ele alınmasına ilişkin iş akışı + +Sanırım artık Go'nun web sunucularını nasıl çalıştırdığını biliyor olmalısınız. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Basit bir web sunucusu oluşturma](03.2.md) +- Sonraki bölüm: [Http paketine girin](03.4.md) diff --git a/tr/03.4.md b/tr/03.4.md new file mode 100644 index 000000000..0ecd88ed8 --- /dev/null +++ b/tr/03.4.md @@ -0,0 +1,207 @@ +# 3.4 Http paketine girin + +Önceki bölümlerde, web'in iş akışını öğrendik ve Go'nun `http` paketinden biraz bahsettik. Bu bölümde, `http` paketindeki iki temel fonksiyon hakkında bilgi edineceğiz: Conn ve ServeMux. + +## Conn'da goroutine + +Normal HTTP sunucularından farklı olarak Go, yüksek eşzamanlılık ve performans elde etmek için Conn tarafından başlatılan her iş için goroutine kullanır, böylece her iş bağımsızdır. + +Go, istemcilerden gelen yeni bağlantıları beklemek için aşağıdaki kodu kullanır. +```Go +c, err := srv.newConn(rw) +if err != nil { + continue +} +go c.serve() +``` +Gördüğünüz gibi, her bağlantı için yeni bir goroutine oluşturuyor ve istekten veri okuyabilen işleyiciyi goroutine'e geçiriyor. + +## Özelleştirilmiş ServeMux + +Önceki bölümlerde conn.server'ı tartışırken Go'nun varsayılan yönlendiricisini kullandık, yönlendirici istek verilerini bir arka uç işleyicisine aktarıyordu. + +Varsayılan yönlendiricinin yapısı: +```Go +type ServeMux struct { + mu sync.RWMutex // eşzamanlılık nedeniyle burada bir muteks kullanmak zorundayız + m map[string]muxEntry // yönlendirici kuralları, bir işleyiciyle eşleşen her dize +} +``` +muxEntry'nin yapısı: +```Go +type muxEntry struct { + explicit bool // tam eşleşme ya da değil + h Handler +} +``` +İşleyici arayüzü: +```Go +type Handler interface { + ServeHTTP(ResponseWriter, *Request) // yönlendi̇rme uygulayıcısı +} +``` +`Handler` bir arayüzdür, ama eğer `sayhelloName` fonksiyonu bu arayüzü uygulamıyorsa, o zaman onu nasıl handler olarak ekledik? Cevap, `http` paketindeki `HandlerFunc` adlı başka bir türde yatmaktadır. Biz `sayhelloName` metodumuzu tanımlamak için `HandlerFunc` çağırdık, böylece `sayhelloName` aynı zamanda `Handler` metodunu da implemente etti. Sanki `HandlerFunc(f)` çağırıyoruz ve `f` fonksiyonu zorla `HandlerFunc` tipine dönüştürülüyor. +```Go +type HandlerFunc func(ResponseWriter, *Request) + +// ServeHTTP f(w, r)'yi çağırır. +func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { + f(w, r) +} +``` +Yönlendirici kurallarını belirledikten sonra yönlendirici işleyicileri nasıl çağırır? + +Yönlendirici, istekleri aldığında `mux.handler.ServeHTTP(w, r)` arayüzünü çağırır. Başka bir deyişle, kendisini uygulayan işleyicilerin `ServeHTTP` arayüzünü çağırır. + +Şimdi, `mux.handler`ın nasıl çalıştığını görelim. +```Go +func (mux *ServeMux) handler(r *Request) Handler { + mux.mu.RLock() + defer mux.mu.RUnlock() + + // Ana bilgisayara özgü kalıp, genel olanlara göre önceliklidir + h := mux.match(r.Host + r.URL.Path) + if h == nil { + h = mux.match(r.URL.Path) + } + if h == nil { + h = NotFoundHandler() + } + return h +} +``` +Yönlendirici, eşlemede kayıtlı ilgili işleyiciyi bulmak için isteğin URL'sini bir anahtar olarak kullanır, ardından verileri işleyecek işlevleri yürütmek için handler.ServeHTTP'yi çağırır. + +Şimdiye kadar varsayılan yönlendiricinin iş akışını anlamış olmalısınız ve Go aslında özelleştirilmiş yönlendiricileri destekler. `ListenAndServe`ün ikinci argümanı özelleştirilmiş yönlendiricileri yapılandırmak içindir. Bu bir `Handler` arayüzüdür. Bu nedenle, `Handler` arayüzünü uygulayan herhangi bir yönlendirici kullanılabilir. + +Aşağıdaki örnekte basit bir yönlendiricinin nasıl uygulanacağı gösterilmektedir. + +```Go +package main + +import ( + "fmt" + "net/http" +) + +type MyMux struct { +} + +func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/" { + sayhelloName(w, r) + return + } + http.NotFound(w, r) + return +} + +func sayhelloName(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "Hello myroute!") +} + +func main() { + mux := &MyMux{} + http.ListenAndServe(":9090", mux) +} +``` + +# Routing - Yönlendirme + +Bir Yönlendirici kullanmak istemiyorsanız, `ListenAndServe` öğesinin ikinci bağımsız değişkenini nil olarak değiştirerek ve en iyi eşleşmeyi bulmak için tüm kayıtlı URL'leri gözden geçiren bir `HandleFunc` işlevi kullanarak URL'leri kaydederek yukarıdaki bölümde yazdıklarımızı yine de elde edebilirsiniz, bu nedenle kayıt sırasına dikkat edilmelidir. + +örnek kod: +```Go +http.HandleFunc("/", views.ShowAllTasksFunc) +http.HandleFunc("/complete/", views.CompleteTaskFunc) +http.HandleFunc("/delete/", views.DeleteTaskFunc) + +// ShowAllTasksFunc, varsayılan ons olan "/" URL'sini işlemek için kullanılır +// TODO http404 hatası ekle +func ShowAllTasksFunc(w http.ResponseWriter, r *http.Request) { + if r.Method == "GET" { + context := db.GetTasks("pending") //silinmemiş görevler istediğinizde true + //db, veritabanı ile etkileşime giren bir pakettir + if message != "" { + context.Message = message + } + homeTemplate.Execute(w, context) + message = "" + } else { + message = "Yönteme izin verilmiyor" + http.Redirect(w, r, "/", http.StatusFound) + } +} +``` +Bu, parametrelendirilmiş yönlendirme gerektirmeyen basit uygulamalar için iyidir, peki ya buna ihtiyacınız olduğunda? Mevcut araç setlerini veya çerçeveleri kullanabilirsiniz, ancak bu kitap golang'da web uygulamaları yazmakla ilgili olduğundan, bu senaryonun nasıl ele alınacağını da öğreteceğiz. + +`HandleFunc` işlevinde eşleşme yapıldığında, URL eşleştirilir, bu nedenle bir yapılacaklar listesi yöneticisi yazdığımızı ve bir görevi silmek istediğimizi varsayalım, böylece bu uygulama için karar verdiğimiz URL `/delete/1` olur, bu nedenle silme URL'sini şu şekilde kaydederiz +`http.HandleFunc("/delete/", views.DeleteTaskFunc)` +`/delete/1` bu URL, "/delete/" URL'si ile diğer URL'lerden daha yakın eşleşir, bu nedenle `r.URL.path` içinde isteğin tüm URL'sini alırız. + +```Go +http.HandleFunc("/delete/", views.DeleteTaskFunc) +// DeleteTaskFunc bir görevi silmek için kullanılır, trash = geri dönüşüm kutusuna taşı, delete = kalıcı silme +func DeleteTaskFunc(w http.ResponseWriter, r *http.Request) { + if r.Method == "DELETE" { + id := r.URL.Path[len("/delete/"):] + if id == "all" { + db.DeleteAll() + http.Redirect(w, r, "/", http.StatusFound) + } else { + id, err := strconv.Atoi(id) + if err != nil { + fmt.Println(err) + } else { + err = db.DeleteTask(id) + if err != nil { + message = "Görev silinirken hata oluştu" + } else { + message = "Görev silindi" + } + http.Redirect(w, r, "/", http.StatusFound) + } + } + } else { + message = "Yönteme izin verilmiyor" + http.Redirect(w, r, "/", http.StatusFound) + } +} +``` + +link: https://github.com/thewhitetulip/Tasks/blob/master/views/views.go#L170-#L195 + +Yukarıdaki yöntemde temel olarak yaptığımız şey, `/delete/` URL'sini işleyen işlevde, `/delete/1` olan compelete URL'sini almak, ardından dizenin bir dilimini almak ve gerçek parametre olan delete kelimesinden sonra başlayan her şeyi çıkarmaktır, bu durumda bu `1`dir. Daha sonra bunu bir tamsayıya dönüştürmek için `strconv` paketini kullanırız ve görevi bu taskID ile sileriz. + +Daha karmaşık senaryolarda da bu yöntemi kullanabiliriz, avantajı herhangi bir üçüncü taraf araç seti kullanmak zorunda olmamamızdır, ancak yine de üçüncü taraf araç setleri kendi başlarına yararlıdır, hangi yöntemi tercih edeceğinize karar vermeniz gerekir. Hiçbir cevap doğru cevap değildir. + + +## Go kod yürütme akışı + +Tüm yürütme akışına bir göz atalım. + +- Çağır `http.HandleFunc` + 1. DefaultServeMux'ın HandleFunc işlevini çağırın + 2. DefaultServeMux Çağrı Tanıtıcısı + 3. DefaultServeMux'un map[string]muxEntry'sine yönlendirici kuralları ekleyin +- Çağır `http.ListenAndServe(":9090", nil)` + 1. Sunucu Oluşturma + 2. Sunucunun ListenAndServe yöntemini çağırın + 3. Portu dinlemek için net.Listen("tcp", addr) çağrısı yapın + 4. Bir döngü başlatın ve döngü gövdesinde istekleri kabul edin + 5. Bir Conn Instantiate edin ve her istek için bir goroutine başlatın: `go c.serve()` + 6. İstek verilerini okuyun: `w, err := c.readRequest()` + 7. İşleyicinin boş olup olmadığını kontrol edin, boşsa DefaultServeMux kullanın + 8. İşleyicinin ServeHTTP'sini çağırın + 9. Bu durumda DefaultServeMux içindeki kodu çalıştırın + 10. URL'ye göre işleyici seçin ve bu işleyici işlevindeki kodu çalıştırın: `mux.handler.ServeHTTP(w, r)` + 11. İşleyici nasıl seçilir: + A. Bu URL için yönlendirici kurallarını kontrol edin + B. Eğer varsa, bu işleyicide ServeHTTP'yi çağırın + C. Aksi takdirde NotFoundHandler'ın ServeHTTP'sini çağırın + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Go web ile nasıl çalışır?](03.3.md) +- Sonraki bölüm: [Özet](03.5.md) diff --git a/tr/03.5.md b/tr/03.5.md new file mode 100644 index 000000000..5000ea3fe --- /dev/null +++ b/tr/03.5.md @@ -0,0 +1,11 @@ +# 3.5 Özet + +Bu bölümde HTTP'yi, DNS çözümleme akışını ve basit bir web sunucusunun nasıl oluşturulacağını tanıttık. Daha sonra `net/http` paketinin kaynak koduna bakarak Go'nun bizim için web sunucularını nasıl uyguladığından bahsettik. + +Umarım artık web geliştirme hakkında çok daha fazla şey biliyorsunuzdur ve Go'da bir web uygulaması oluşturmanın oldukça kolay ve esnek olduğunu görmelisiniz. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Http paketine girin](03.4.md) +- Sonraki bölüm: [Kullanıcı formu](04.0.md) From 7c5008241f2cdde0278c5aa0295cde826b407e77 Mon Sep 17 00:00:00 2001 From: mstgnz Date: Fri, 12 Jan 2024 22:55:44 +0300 Subject: [PATCH 8/8] update Turkish for 04.chapter --- tr/04.0.md | 23 ++++++++ tr/04.1.md | 112 ++++++++++++++++++++++++++++++++++++ tr/04.2.md | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++ tr/04.3.md | 68 ++++++++++++++++++++++ tr/04.4.md | 62 ++++++++++++++++++++ tr/04.5.md | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ tr/04.6.md | 11 ++++ 7 files changed, 601 insertions(+) create mode 100644 tr/04.0.md create mode 100644 tr/04.1.md create mode 100644 tr/04.2.md create mode 100644 tr/04.3.md create mode 100644 tr/04.4.md create mode 100644 tr/04.5.md create mode 100644 tr/04.6.md diff --git a/tr/04.0.md b/tr/04.0.md new file mode 100644 index 000000000..1bee0f4e3 --- /dev/null +++ b/tr/04.0.md @@ -0,0 +1,23 @@ +# 4 Kullanıcı formu + +Kullanıcı formu, web uygulamaları geliştirirken çok yaygın olarak kullanılan bir şeydir. İstemciler ve sunucular arasında iletişim kurma olanağı sağlar. Eğer bir web geliştiricisiyseniz formlara çok aşina olmalısınız; eğer bir `C/C++` programcısıysanız şunu sormak isteyebilirsiniz: kullanıcı formu nedir? + +Form, form öğelerini içeren bir alandır. Kullanıcılar metin kutuları, açılır listeler, radyo düğmeleri, onay kutuları vb. gibi form öğelerine bilgi girebilir. Formları tanımlamak için `
` form etiketini kullanırız. + + + ... + giriş elemanları + ... +
+ +Go'nun zaten kullanıcı formlarıyla başa çıkmak için birçok kullanışlı fonksiyonu bulunmaktadır. HTTP isteklerinde form verilerini kolayca alabilir ve kendi web uygulamalarınıza entegre etmek de kolaydır. 4.1 bölümünde, Go'da form verilerini nasıl ele alacağımızı konuşacağız. Ayrıca, istemci tarafından gelen herhangi bir veriye güvenemeyeceğinizden, veriyi kullanmadan önce ilk olarak doğrulamanız gerekir. 4.2 bölümünde form verilerini nasıl doğrulayacağımıza dair bazı örnekleri inceleyeceğiz. + +HTTP'nin durumsuz (stateless) olduğunu söylüyoruz. Belirli formların aynı kullanıcıdan geldiğini nasıl belirleyebiliriz? Ve nasıl yaparız ki bir form sadece bir kez gönderilebilsin? Hem 4.3 hem de 4.4 bölümlerinde çerezlerle ilgili bazı detaylara bakacağız (bir çerez, isteğin sunucuya gönderildiği zaman talep başlığına eklenebilen bilgidir). + +Formlar için başka yaygın bir kullanım durumu da dosyaları yüklemektir. 4.5 bölümünde, Go'da bunu nasıl yapacağınızı ve dosya yükleme öncesinde dosya yükleme boyutunu nasıl kontrol edeceğinizi öğreneceksiniz. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Bölüm 3 Özeti](03.5.md) +- Sonraki bölüm: [Form girdilerini işle](04.1.md) diff --git a/tr/04.1.md b/tr/04.1.md new file mode 100644 index 000000000..208e59747 --- /dev/null +++ b/tr/04.1.md @@ -0,0 +1,112 @@ +# 4.1 Form girdilerini işle + +Başlamadan önce, projenizin klasöründe `login.gtpl` olarak kaydedilmiş tipik bir kullanıcı formunun basit bir örneğine bakalım. + +```html + + + + + +
+ Username: + Password: + +
+ + +``` +Bu form, sunucuda `/login`'e gönderilecek. Kullanıcı giriş düğmesine tıkladıktan sonra, veriler sunucu tarafından yönlendirici tarafından kaydedilen `login` işleyicisine gönderilecek. Ardından, bu işlemde POST yöntemi mi yoksa GET yöntemi mi kullanıldığını bilmemiz gerekiyor. + +Bunu, `http` paketi kullanarak kolayca öğrenebiliriz. Hadi form verilerini giriş sayfasında nasıl ele alacağımıza bakalım. + +```Go +package main + +import ( + "fmt" + "html/template" + "log" + "net/http" + "strings" +) + +func sayhelloName(w http.ResponseWriter, r *http.Request) { + r.ParseForm() + // Geçirilen URL parametrelerini ayrıştır, ardından POST gövdesi (isteğin gövdesi) için yanıt paketini ayrıştır + // Dikkat: ParseForm yöntemini çağırmazsanız, aşağıdaki veriler elde edilemez + fmt.Println(r.Form) // Sunucu tarafında bilgileri yazdır. + fmt.Println("path", r.URL.Path) + fmt.Println("scheme", r.URL.Scheme) + fmt.Println(r.Form["url_long"]) + for k, v := range r.Form { + fmt.Println("key:", k) + fmt.Println("val:", strings.Join(v, "")) + } + fmt.Fprintf(w, "Hello astaxie!") // Veriyi yanıta yaz +} + +func login(w http.ResponseWriter, r *http.Request) { + fmt.Println("method:", r.Method) // GET isteği yöntemini al + if r.Method == "GET" { + t, _ := template.ParseFiles("login.gtpl") + t.Execute(w, nil) + } else { + r.ParseForm() + // oturum açma işleminin mantıksal kısmı + fmt.Println("username:", r.Form["username"]) + fmt.Println("password:", r.Form["password"]) + } +} + +func main() { + http.HandleFunc("/", sayhelloName) // yönlendirici kuralını ayarla + http.HandleFunc("/login", login) + err := http.ListenAndServe(":9090", nil) // dinleme portunu ayarla + if err != nil { + log.Fatal("ListenAndServe: ", err) + } +} + +``` +"Işte isteği almak için `r.Method`'u kullandık ve bu, bir HTTP fiili - "GET", "POST", "PUT", vb. - döndürür. + +`login` fonksiyonunda, `r.Method`'u kullanarak bu bir giriş sayfası mı yoksa giriş işleme mantığı mı olduğunu kontrol ediyoruz. Başka bir deyişle, kullanıcının sayfayı sadece açıp açmadığını mı yoksa giriş yapmaya mı çalıştığını mı kontrol ediyoruz. Serve, talep GET yöntemi kullanılarak geldiğinde sayfayı gösterir ve talep POST yöntemi kullanıldığında giriş mantığını yürütür. + +Tarayıcınızda `http://127.0.0.1:9090/login` adresini açtıktan sonra aşağıdaki arayüzü görmelisiniz." + +![](images/4.1.login.png?raw=true) + +Şekil 4.1 Kullanıcı giriş arayüzü + +Sunucu, `r.ParseForm()` çağrılmadığı sürece, kullanıcı adı ve şifreyi girene kadar herhangi bir şey yazdırmayacaktır, çünkü işleyici formu ayrıştırmaz. Hadi `fmt.Println("username:", r.Form["username"])` öncesine `r.ParseForm()` ekleyelim, programımızı derleyip tekrar test edelim. Şimdi bilgilerin sunucu tarafında yazdırıldığını göreceksiniz. + +`r.Form`, örneğin URL'deki sorgu dizesi ve POST ile PUT'taki veriler gibi, tüm istek argümanlarını içerir. Veriler çakışıyorsa, yani aynı adı taşıyan parametreler varsa, sunucu veriyi çoklu değer içeren bir dilime kaydedecektir. Go belgelerine göre, Go, GET ve POST isteklerinden gelen verileri farklı yerlere kaydedecektir. + +`login.gtpl` dosyasındaki formun aksiyon değerini `http://127.0.0.1:9090/login?username=astaxie` olarak değiştirin, tekrar test edin ve dilimin sunucu tarafında yazdırıldığını göreceksiniz. + +![](images/4.1.slice.png?raw=true) + +Şekil 4.2 Sunucu, istek verilerini yazdırır + +`request.Form`'un türü `url.Values`'dir. Veriyi `key=value` formatında kaydeder. + +```Go + v := url.Values{} + v.Set("name", "Ava") + v.Add("friend", "Jess") + v.Add("friend", "Sarah") + v.Add("friend", "Zoe") + // v.Encode() == "name=Ava&friend=Jess&friend=Sarah&friend=Zoe" + fmt.Println(v.Get("name")) + fmt.Println(v.Get("friend")) + fmt.Println(v["friend"]) +``` + +**İpucu:** İstekler, `FormValue()` yöntemini kullanarak form verilerine erişme yeteneğine sahiptir. Örneğin, `r.Form["username"]`'ı `r.FormValue("username")` olarak değiştirebilirsiniz ve Go otomatik olarak `r.ParseForm`'u çağırır. Aynı isme sahip argümanlar varsa ilk değeri döndüğüne dikkat edin ve böyle bir argüman yoksa boş bir dize döndürür. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Kullanıcı formu](04.0.md) +- Sonraki bölüm: [Girişlerin doğrulanması](04.2.md) diff --git a/tr/04.2.md b/tr/04.2.md new file mode 100644 index 000000000..55bbaa8ab --- /dev/null +++ b/tr/04.2.md @@ -0,0 +1,160 @@ +# 4.2 Girişlerin doğrulanması + +Web geliştirmede en önemli prensiplerden biri, istemci tarafındaki kullanıcı formlarından gelen her şeye güvenmemeniz gerektiğidir. Kullanmadan önce tüm gelen verileri doğrulamanız gerekmektedir. Bu basit ama kritik bir problem, birçok web sitesini etkileyebilir. + +Kullanılan yaygın iki form verisi doğrulama yöntemi bulunmaktadır. İlk olarak, ön uçta JavaScript doğrulaması ve ikinci olarak, arka uçta sunucu doğrulamasıdır. Bu bölümde, web geliştirmede sunucu tarafı doğrulamasını ele alacağız. + +## Gerekli alanlar + +Bazen, kullanıcılardan bazı alanları girmelerini istiyoruz ancak alanı tamamlamayı unutabilirler. Örneğin, önceki bölümde bir kullanıcı adı gerektirdiğimizde. Kullanıcıların bir şeyler girdiğinden emin olmak için `len` fonksiyonunu kullanabilirsiniz. + +```Go + if len(r.Form["username"][0])==0{ + // boş alan kodu + } +``` +`r.Form`, boş olduklarında farklı form eleman türlerini farklı şekilde işler. Boş metin kutuları, metin alanları ve dosya yüklemeleri için boş bir dize döndürür; radyo düğmeleri ve onay kutuları için ise karşılık gelen öğeleri oluşturmaz. Ona erişmeye çalışırsanız hatalar alırsınız. Bu nedenle, değer mevcut değilse her zaman boş bir değer döndüreceği için `r.Form.Get()` kullanmak daha güvenlidir. Diğer taraftan, `r.Form.Get()` yalnızca bir alan değerini bir seferde alabilir, bu nedenle değer haritasını almak için `r.Form`'u kullanmanız gerekir. + +## Sayılar + +"Bazen, diğer metinler yerine alan değeri için sayılar gerekebilir. Örneğin, bir kullanıcının yaşını yalnızca tamsayı formunda, yani 50 veya 10 olarak istediğinizi düşünelim, "yaşlı" veya "genç adam" değil. Pozitif bir sayı gerekiyorsa, değeri önce `int` türüne dönüştürebilir ve ardından işleyebiliriz." + +```Go + getint,err:=strconv.Atoi(r.Form.Get("age")) + if err!=nil{ + // Sayıya dönüştürüldüğünde hata oluşur, bu bir sayı olmayabilir + } + + // Sayının aralığını kontrol et + if getint >100 { + // çok büyük + } +``` + +Bunu yapmanın başka bir yolu da düzenli ifadeleri kullanmaktır. + +```Go + if m, _ := regexp.MatchString("^[0-9]+$", r.Form.Get("age")); !m { + return false + } +``` +Yüksek performans amacıyla, düzenli ifadeler genellikle etkili değildir, ancak genellikle yeterince hızlı olan basit düzenli ifadeler vardır. Düzenli ifadelerle tanışıksanız, veri doğrulamanın çok uygun bir yolu olduğunu fark edeceksiniz. Go, [RE2](http://code.google.com/p/re2/wiki/Syntax) kullanır, bu nedenle tüm UTF-8 karakterleri desteklenir. + +## Çinli + +Bazen kullanıcılardan Çin isimlerini girmelerini isteriz ve bu isimlerin rastgele karakterler yerine gerçekten Çince karakterler içerdiğini doğrulamamız gerekir. Çin doğrulaması için düzenli ifadeler tek yol olabilir. + +```Go +if m, _ := regexp.MatchString("^[\\x{4e00}-\\x{9fa5}]+$", r.Form.Get("gerçek adı")); !m { + return false +} +``` + +## İngilizce Harfler + +Bazen kullanıcılardan yalnızca İngilizce harfler girmelerini isteriz. Örneğin, birinin İngilizce adını, örneğin astaxie yerine asta谢 gibi, istiyor olabiliriz. Bu doğrulamayı gerçekleştirmek için düzenli ifadeleri kolayca kullanabiliriz. + +```Go +if m, _ := regexp.MatchString("^[a-zA-Z]+$", r.Form.Get("engname")); !m { + return false +} + +``` +## E-mail adresi + +Eğer kullanıcıların geçerli e-posta adresleri girdiğini kontrol etmek istiyorsanız, aşağıdaki düzenli ifadeyi kullanabilirsiniz: + +```Go + if m, _ := regexp.MatchString(`^([\w\.\_]{2,10})@(\w{1,}).([a-z]{2,4})$`, r.Form.Get("email")); !m { + fmt.Println("hayır") + }else{ + fmt.Println("evet") + } +``` + +## Açılır Liste + +Varsayalım ki açılır listemizden belirli bir öğe talep ediyoruz, ancak yerine bir hacker tarafından üretilen bir değer alıyoruz. Bunu nasıl engelleriz? + +Suppose we have the following ` + + + + +``` +Girişimizi temizlemek için aşağıdaki stratejiyi kullanabiliriz: +```Go + slice:=[]string{"elma","armut","muz"} + + for _, v := range slice { + if v == r.Form.Get("meyveler") { + return true + } + } + return false +``` +Yukarıda gösterdiğim tüm fonksiyonlar, dilimler ve haritalar üzerinde işlem yapmak için olan açık kaynaklı projemde bulunmaktadır: [https://github.com/astaxie/beeku](https://github.com/astaxie/beeku) + +## Radio buttons + +Eğer kullanıcının erkek veya kadın olduğunu bilmek istiyorsak, bir radyo düğmesi kullanabiliriz; erkek için 1, kadın için 2 döndürebiliriz. Ancak, HTTP üzerine ilk kitabını okuyan küçük bir çocuk, size bir 3 göndermeye karar verirse, programınız istisna fırlatır mı? Gördüğünüz gibi, radyo düğmemiz için beklenen değerlerin yalnızca döndürülmesini sağlamak için açılır listemizde yaptığımız gibi aynı yöntemi kullanmamız gerekiyor. + +```html + Erkek + Kadın + +``` + +Ve girişi doğrulamak için aşağıdaki kodu kullanıyoruz: + +```Go + slice:=[]string{"1","2"} + + for _, v := range slice { + if v == r.Form.Get("cinsiyet") { + return true + } + } + return false +``` +## Onay Kutuları + +Varsayalım ki kullanıcı ilgi alanları için bazı onay kutuları var ve burada da gereksiz değerlere izin vermek istemiyorsunuz. Bunları aşağıdaki gibi doğrulayabilirsiniz: + +```html + Football + Basketball + Tennis +``` + +Bu durumda, düğme ve onay kutusu girişlerini doğrulamaktan biraz farklı olan temizleme işlemi, burada onay kutularından bir dilim alıyoruz. + +```Go +slice:=[]string{"football","basketball","tennis"} +a:=Slice_diff(r.Form["interest"],slice) +if a == nil{ + return true +} + +return false +``` +## Tarih ve Saat + +Kullanıcıların geçerli tarih veya saatleri girmesini istiyorsanız, Go'nun `time` paketini yıl, ay ve günü karşılık gelen zamanlara dönüştürmek için kullanabilirsiniz. Sonrasında bunu kontrol etmek kolaydır. + +```Go + t := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC) + fmt.Printf("Go, %s tarihinde piyasaya sürüldü\n", t.Local()) +``` + +Zamanı elde ettikten sonra, ihtiyaçlarınıza bağlı olarak daha fazla işlem için `time` paketini kullanabilirsiniz. + +Bu bölümde, sunucu tarafında form verilerini doğrulamanın bazı yaygın yöntemlerini tartıştık. Umarım şimdi Go'da veri doğrulama konusunda daha fazla bilgi sahibi oldunuz, özellikle düzenli ifadeleri nasıl avantajınıza kullanacağınızı anladınız. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Form girdilerini işle](04.1.md) +- Sonraki bölüm: [Çapraz Site Komut Dosyalama](04.3.md) diff --git a/tr/04.3.md b/tr/04.3.md new file mode 100644 index 000000000..ca8c2aff6 --- /dev/null +++ b/tr/04.3.md @@ -0,0 +1,68 @@ +# 4.3 Çapraz Site Komut Dosyalama + +Günümüzdeki web siteleri, kullanıcı deneyimini artırmak amacıyla çok daha dinamik içeriklere sahiptir, bu da her bireyin davranışına bağlı olarak dinamik bilgi sağlamamız gerektiği anlamına gelir. Ne yazık ki, dinamik web siteleri "Cross-Site Scripting" (veya "XSS" olarak bilinen) olarak bilinen kötü niyetli saldırılara karşı hassastır. Statik web siteleri Cross-Site Scripting'e karşı savunmasız değildir. + +Saldırganlar genellikle JavaScript, VBScript, ActiveX veya Flash gibi kötü niyetli betikleri sitelere açık olan yerlere enjekte ederler. Betiklerini başarıyla enjekte ettikten sonra, kullanıcı bilgileri çalınabilir ve web siteniz istenmeyen içerikle dolabilir. Saldırganlar aynı zamanda kullanıcı ayarlarını istedikleri gibi değiştirebilirler. + +Bu tür bir saldırıyı önlemek istiyorsanız, aşağıdaki iki yaklaşımı birleştirmelisiniz: + +- Önceki bölümde konuştuğumuz gibi, kullanıcılardan gelen tüm verilerin doğrulanması. +- Tarayıcılarda enjekte edilmiş betiklerin çalışmasını önlemek için müşterilere gönderilecek verileri dikkatlice işleme koymak. + +Peki, bunları Go dilinde nasıl yapabiliriz? Neyse ki, `html/template` paketi, verileri güvenli bir şekilde çıkarmak için bazı kullanışlı fonksiyonlara sahiptir: + +- `func HTMLEscape(w io.Writer, b []byte)` fonksiyonu, `b` slice'ını güvenli bir şekilde kaçırarak (`escape` ederek) `w`'ye yazan bir işlevdir. +- `func HTMLEscapeString(s string) string` fonksiyonu, `s` string'ini kaçırarak (`escape` ederek) elde edilen bir string'i döndürür. +- `func HTMLEscaper(args ...interface{}) string` fonksiyonu, birden fazla argümandan elde edilen bir string'i kaçırarak (`escape` ederek) döndürür. + +4.1 bölümündeki örneği değiştirelim: +```Go + fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // sunucu tarafında yazdıralım + fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) + template.HTMLEscape(w, []byte(r.Form.Get("username"))) // cevabı dön +``` +Eğer birisi kullanıcı adını `` olarak girmeye çalışırsa, tarayıcıda şu içeriği göreceğiz: + +![](images/4.3.escape.png?raw=true) + +"Şekil 4.3 Kaçırıldıktan Sonraki JavaScript + +`html/template` paketindeki fonksiyonlar, tüm HTML etiketlerini kaçırmak için size yardımcı olur. Peki ya sadece ``'i tarayıcılara yazdırmak istiyorsanız? O zaman bunun yerine `text/template` kullanmalısınız." +```Go + import "text/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") +``` +Çıktı: +```html +Hello, ! +``` +Ya da `template.HTML` türünü kullanabilirsiniz: +Değişken içeriği, türü `template.HTML` ise kaçırılmayacaktır. +```Go + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", template.HTML("")) +``` +Çıktı: +```html +Hello, ! +``` +Kaçırma ile ilgili bir başka örnek daha: +```Go + import "html/template" + ... + t, err := template.New("foo").Parse(`{{define "T"}}Hello, {{.}}!{{end}}`) + err = t.ExecuteTemplate(out, "T", "") +``` +Çıktı: +```html +Hello, <script>alert('you have been pwned')</script>! +``` +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Girişlerin doğrulanması](04.2.md) +- Sonraki bölüm: [Çoğaltılmış gönderimler](04.4.md) diff --git a/tr/04.4.md b/tr/04.4.md new file mode 100644 index 000000000..25569cf6a --- /dev/null +++ b/tr/04.4.md @@ -0,0 +1,62 @@ +# 4.4 Çoğaltılmış gönderimler + +Belki de daha önce bazı blogları veya BBS'leri gördünüz mü bilmiyorum, ancak size söyleyebilirim ki, bu durum genellikle kullanıcıların aynı içeriği gönderen formları çoğaltmasından kaynaklanır. Çoğaltılmış gönderimlere sebep olan birçok şey var; bazen kullanıcılar sadece gönder düğmesine çift tıklarlar, ya da gönderdikten sonra bazı içerikleri değiştirmek isteyip geri düğmesine basarlar. Bazı durumlarda, bu kasıtlı olarak kötü niyetli kullanıcıların eylemleri sonucunda gerçekleşir. Çoğaltılmış gönderimlerin birçok soruna yol açabileceğini görmek kolaydır. Bu nedenle, bunu önlemek için etkili yöntemler kullanmalıyız. + +Çözüm, formunuza benzersiz bir belirteç içeren gizli bir alan eklemek ve gelen verileri işlemeden önce her zaman bu belirteci kontrol etmektir. Ayrıca, bir formu göndermek için Ajax kullanıyorsanız, formun gönderildikten sonra gönder düğmesini devre dışı bırakmak için JavaScript kullanın. + +Hadi 4.2 bölümündeki örneği geliştirelim: + +```html + Football + Basketball + Tennis + Username: + Password: + + +``` + +Token'ı oluşturmak için bir MD5 özeti (zaman damgası) kullanıyoruz ve bunu istemci tarafındaki formdaki gizli bir alana ve sunucu tarafındaki bir oturum çerezi (Bölüm 6) üzerine ekledik. Bu token'ı kullanarak bu formun gönderilip gönderilmediğini kontrol edebiliriz. + +```Go +func login(w http.ResponseWriter, r *http.Request) { + fmt.Println("method:", r.Method) // GET isteği metodu + if r.Method == "GET" { + crutime := time.Now().Unix() + h := md5.New() + io.WriteString(h, strconv.FormatInt(crutime, 10)) + token := fmt.Sprintf("%x", h.Sum(nil)) + + t, _ := template.ParseFiles("login.gtpl") + t.Execute(w, token) + } else { + // log in request + r.ParseForm() + token := r.Form.Get("token") + if token != "" { + // token doğrulama + } else { + // token yoksa hata üret + } + fmt.Println("username length:", len(r.Form["username"][0])) + fmt.Println("username:", template.HTMLEscapeString(r.Form.Get("username"))) // consola yazdır + fmt.Println("password:", template.HTMLEscapeString(r.Form.Get("password"))) + template.HTMLEscape(w, []byte(r.Form.Get("username"))) // cevabı dön + } +} + +``` + +![](images/4.4.token.png?raw=true) + +Şekil 4.4 Token ekledikten sonra tarayıcıdaki içerik + +Bu sayfayı yenileyebilirsiniz ve her seferinde farklı bir token göreceksiniz. Bu, her formun benzersiz olmasını sağlar. + +Şu an için, formlarınıza token ekleyerek birçok çoğaltılmış gönderim saldırısını önleyebilirsiniz, ancak bu tür tüm aldatıcı saldırıları önleyemez. Yapılması gereken daha çok iş var. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Çapraz Site Komut Dosyalama](04.3.md) +- Sonraki bölüm: [Dosya Yükleme](04.5.md) diff --git a/tr/04.5.md b/tr/04.5.md new file mode 100644 index 000000000..6d27eb906 --- /dev/null +++ b/tr/04.5.md @@ -0,0 +1,165 @@ +# 4.5 Dosya Yükleme + +Örneğin Instagram gibi bir web siteniz var ve kullanıcıların güzel fotoğraflarını yüklemelerini istiyorsunuz. Bu işlevselliği nasıl uygularsınız? + +Fotoğraf yüklemek için kullanmak istediğiniz forma `enctype` özelliğini eklemeniz gerekmektedir. Bu özelliğin üç olası değeri vardır: + +``` +application/x-www-form-urlencoded (Varsayılan) Yüklemeden önce tüm karakterleri dönüştür. +multipart/form-data Dönüştürme yok. Formunuz dosya yükleme kontrolleri içeriyorsa bu değeri kullanmalısınız. +text/plain Boşlukları "+" ile değiştir, ancak özel karakterler için dönüştürme yapma. +``` + +Bu nedenle, bir dosya yükleme formunun HTML içeriği şu gibi görünmelidir: + +```html + + + Upload file + + +
+ + + +
+ + +``` + +Bu formu işlemek için sunucu tarafında bir işlev eklememiz gerekiyor. + +```Go +http.HandleFunc("/upload", upload) + +// yükleme mantığı +func upload(w http.ResponseWriter, r *http.Request) { + fmt.Println("method:", r.Method) + if r.Method == "GET" { + crutime := time.Now().Unix() + h := md5.New() + io.WriteString(h, strconv.FormatInt(crutime, 10)) + token := fmt.Sprintf("%x", h.Sum(nil)) + + t, _ := template.ParseFiles("upload.gtpl") + t.Execute(w, token) + } else { + r.ParseMultipartForm(32 << 20) + file, handler, err := r.FormFile("uploadfile") + if err != nil { + fmt.Println(err) + return + } + defer file.Close() + fmt.Fprintf(w, "%v", handler.Header) + f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + fmt.Println(err) + return + } + defer f.Close() + io.Copy(f, file) + } +} +``` + +Gördüğünüz gibi dosyaları yüklemek için `r.ParseMultipartForm`u çağırmamız gerekiyor. ParseMultipartForm işlevi "maxMemory" bağımsız değişkenini alır. `ParseMultipartForm`u çağırdıktan sonra dosya `maxMemory` boyutunda sunucu belleğine kaydedilecektir. Dosya boyutu 'maxMemory'den büyükse verilerin geri kalanı sistem geçici dosyasına kaydedilecektir. Dosya tanıtıcısını almak için "r.FormFile"ı, dosya sisteminize kaydetmek için "io.Copy"yi kullanabilirsiniz. + +Formdaki dosya dışı diğer alanlara eriştiğinizde `r.ParseForm`u çağırmanıza gerek yoktur çünkü Go gerektiğinde onu arayacaktır. Ayrıca, 'ParseMultipartForm'u bir kez çağırmak yeterlidir; birden fazla çağrı hiçbir fark yaratmaz. + +Dosyaları yüklemek için aşağıdaki üç adımı kullanırız: + +1. Formunuza `enctype="multipart/form-data"` ekleyin. +2. Dosyayı belleğe veya geçici bir dosyaya kaydetmek için sunucu tarafında `r.ParseMultipartForm`u çağırın. +3. Dosya tanıtıcısını almak ve dosya sistemine kaydetmek için `r.FormFile`ı çağırın. + +Dosya işleyicisi 'multipart.FileHeader'dır. Aşağıdaki yapıyı kullanır: + +```Go +type FileHeader struct { + Filename string + Header textproto.MIMEHeader + // filtrelenmiş veya dışa aktarılmamış alanlar içeriyor +} +``` + +![](images/4.5.upload2.png?raw=true) + +Şekil 4.5 Dosyayı aldıktan sonra sunucudaki bilgileri yazdırın. + +## İstemciler dosya yükler + +Dosya yüklemek için form kullanmanın bir örneğini gösterdim. Go'ya dosya yüklemek için bir müşteri formunun kimliğine de bürünebiliriz. + +```Go +package main + +import ( + "bytes" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "net/http" + "os" +) + +func postFile(filename string, targetUrl string) error { + bodyBuf := &bytes.Buffer{} + bodyWriter := multipart.NewWriter(bodyBuf) + + // bu adım çok önemli + fileWriter, err := bodyWriter.CreateFormFile("uploadfile", filename) + if err != nil { + fmt.Println("ara belleğe yazma hatası") + return err + } + + // open file handle + fh, err := os.Open(filename) + if err != nil { + fmt.Println("dosya açılırken hata oluştu") + return err + } + defer fh.Close() + + //iocopy + _, err = io.Copy(fileWriter, fh) + if err != nil { + return err + } + + contentType := bodyWriter.FormDataContentType() + bodyWriter.Close() + + resp, err := http.Post(targetUrl, contentType, bodyBuf) + if err != nil { + return err + } + defer resp.Body.Close() + resp_body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return err + } + fmt.Println(resp.Status) + fmt.Println(string(resp_body)) + return nil +} + +// örnek kullanım +func main() { + target_url := "http://localhost:9090/upload" + filename := "./astaxie.pdf" + postFile(filename, target_url) +} +``` + +Yukarıdaki örnek, dosyaları yüklemek için bir istemcinin nasıl kullanılacağını gösterir. Dosyaları önbelleğe yazmak için `multipart.Write`ı kullanır ve bunları POST yöntemi aracılığıyla sunucuya gönderir. + +Verilere yazılması gereken kullanıcı adı gibi başka alanlarınız varsa gerektiği gibi `multipart.WriteField`ı çağırın. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Çoğaltılmış gönderimler](04.4.md) +- Sonraki bölüm: [Özet](04.6.md) diff --git a/tr/04.6.md b/tr/04.6.md new file mode 100644 index 000000000..162619812 --- /dev/null +++ b/tr/04.6.md @@ -0,0 +1,11 @@ +# 4.6 Özet + +Bu bölümde temel olarak Go'da form verilerinin nasıl işleneceği, kullanıcılarda oturum açma ve dosya yükleme gibi çeşitli örnekleri inceledik. Ayrıca kullanıcı verilerinin doğrulanmasının web sitesi güvenliği açısından son derece önemli olduğunu vurguladık ve bir bölümü düzenli ifadelerle verilerin nasıl filtreleneceğinden bahsetmek için kullandık. + +Umarım artık istemci ve sunucu arasındaki iletişim süreci hakkında daha fazla bilgi sahibisinizdir. + +## Linkler + +- [İçerik](preface.md) +- Önceki bölüm: [Dosya yükleme](04.5.md) +- Sonraki bölüm: [Veritabanı](05.0.md)