Dekoratör olarak Middleware
Coddy'nin GO Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 97 / 107.
Middleware, Go'daki Decorator deseninin en pratik uygulamalarından biridir. Web geliştirmede, middleware fonksiyonları; logging, kimlik doğrulama veya zamanlama gibi ortak işlevleri (cross-cutting concerns), orijinal handler'ı değiştirmeden eklemek için HTTP handler'larını sarmalar.
Temel içgörü, middleware'in bir işleyici (handler) alıp eklenmiş davranışla yeni bir işleyici döndürmesidir. Bu, Decorator desenini tam olarak takip eder; giriş ve çıkışta aynı arayüz, geliştirilmiş işlevsellik ile:
type Handler func(request string) string
func LoggingMiddleware(next Handler) Handler {
return func(request string) string {
result := next(request)
return "[LOG] " + result
}
}
func AuthMiddleware(next Handler) Handler {
return func(request string) string {
return "[AUTH] " + next(request)
}
}Her bir ara katman (middleware), zincirdeki bir sonraki işleyiciyi (handler) sarmalayarak, sarmalanmış işleyiciyi çağırmadan önce veya sonra kendi davranışını ekler. Birden fazla ara katmanı bir araya getirerek üst üste ekleyebilirsiniz:
func MainHandler(request string) string {
return "Response to: " + request
}
handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] Response to: GET /usersSarma sırası, yürütme sırasını belirler; en dıştaki ara yazılım (middleware) ilk önce çalışır. Bu desen, Go web çerçevelerinde istek işleme boru hatları (pipelines) için yaygın olarak kullanılır ve çekirdek işleyici (handler) mantığına dokunmadan işlevsellik eklemeyi veya kaldırmayı kolaylaştırır.
Görev
KolayMiddleware as Decorator (Dekoratör Olarak Ara Katman) desenini kullanarak bir istek işleme hattı (pipeline) oluşturalım! Ara katman fonksiyonlarının, temel işleyici (handler) mantığını değiştirmeden zamanlama, doğrulama ve biçimlendirme gibi özellikler eklemek için işleyicileri sarmaladığı bir sistem oluşturacaksınız.
Kodunuzu üç dosya halinde düzenleyeceksiniz:
handler.go: İşleyici tipinizi ve istekleri işleyen temel işleyiciyi tanımlayın.Bir istek dizesi alan ve bir yanıt dizesi döndüren bir fonksiyon olarak
Handlertipi oluşturun:type Handler func(request string) stringAldığı herhangi bir istek için
Processed: [request]döndüren birBaseHandlerfonksiyonu uygulayın.middleware.go: İşleyicileri ek davranışlarla sarmalayan ara katman fonksiyonlarınızı oluşturun.Her biri bir
Handleralan ve yeni birHandlerdöndüren üç ara katman fonksiyonu oluşturun:TimingMiddleware— yanıtı zamanlama bilgisiyle sarmalar ve[TIMING] [response]döndürürValidationMiddleware— doğrulama öneki ekler ve[VALID] [response]döndürürUppercaseMiddleware— tüm yanıtı büyük harfe dönüştürür
Her ara katman, sarmalanmış işleyiciyi çağırmalı ve sonucunu geliştirmelidir.
main.go: Ara katmanları birleştirerek bir işleme hattı oluşturun.Uygulanacak ara katman katmanı sayısını okuyun. Ardından her katman için ara katman tipini (
timing,validationveyauppercase) okuyun ve mevcut işleyicinizi bu ara katmanla sarmalayın. Bunları okundukları sırayla uygulayın; okunan ilk ara katman en dıştaki sarmalayıcı olur.Hattı oluşturduktan sonra istek dizesini okuyun, oluşturduğunuz işleyici aracılığıyla işleyin ve sonucu yazdırın.
Aşağıdaki girdiler sağlanacaktır:
- Satır 1: Ara katman katmanı sayısı
- Sonraki satırlar: Satır başına bir ara katman tipi (
timing,validationveyauppercase) - Son satır: İşlenecek istek
Örneğin, şu girdi verildiğinde:
2
timing
validation
GET /usersÇıktınız şu olmalıdır:
[TIMING] [VALID] Processed: GET /usersVe şu girdi verildiğinde:
3
uppercase
timing
validation
POST /dataÇıktınız şu olmalıdır:
[TIMING] [VALID] PROCESSED: POST /DATAVe şu girdi verildiğinde:
1
uppercase
hello worldÇıktınız şu olmalıdır:
PROCESSED: HELLO WORLDVe şu girdi verildiğinde:
0
simple requestÇıktınız şu olmalıdır:
Processed: simple requestAra katman sarmalama sırasının çıktıyı nasıl etkilediğine dikkat edin; uygulanan ilk ara katman (en dıştaki) ilk çalışır ve tüm iç ara katmanlardan gelen nihai sonucu görür. İkinci örnekteki uppercase ara katmanı en son (en içte) çalışır, bu nedenle zamanlama ve doğrulama kendi öneklerini eklemeden önce temel yanıtı büyük harfe dönüştürür!
Kopya kağıdı
Go'da middleware (ara katman), orijinal işleyiciyi değiştirmeden işlevsellik eklemek için işleyicileri sarmalayarak Decorator (Dekoratör) desenini takip eder.
Bir işleyici (handler) tipini fonksiyon olarak tanımlayın:
type Handler func(request string) stringMiddleware fonksiyonları bir işleyici alır ve geliştirilmiş davranışa sahip yeni bir işleyici döndürür:
func LoggingMiddleware(next Handler) Handler {
return func(request string) string {
result := next(request)
return "[LOG] " + result
}
}
func AuthMiddleware(next Handler) Handler {
return func(request string) string {
return "[AUTH] " + next(request)
}
}Birden fazla middleware'i birleştirerek üst üste ekleyin:
func MainHandler(request string) string {
return "Response to: " + request
}
handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// Output: [LOG] [AUTH] Response to: GET /usersEn dıştaki middleware ilk olarak çalışır ve yürütme sırasını belirler. Her middleware, sarmalanmış işleyiciyi çağırmadan önce veya sonra davranış ekleyebilir.
Kendin dene
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Middleware katmanlarının sayısını oku
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// Middleware türlerini oku
middlewareTypes := make([]string, n)
for i := 0; i < n; i++ {
line, _ := reader.ReadString('\n')
middlewareTypes[i] = strings.TrimSpace(line)
}
// İsteği oku
request, _ := reader.ReadString('\n')
request = strings.TrimSpace(request)
// TODO: BaseHandler ile başla
// TODO: İşleyiciyi middleware ile sarmalayarak işlem hattını (pipeline) oluştur
// Middleware'leri sırayla uygula - okunan ilk middleware en dıştaki sarmalayıcı olur
// İpucu: Bunu başarmak için middlewareTypes üzerinde TERS sırada döngü kurun
// Uygulanan son middleware en dıştaki olacaktır (ilk çalışır)
// TODO: İsteği oluşturulan işleyici aracılığıyla işle ve sonucu yazdır
fmt.Println("TODO: implement pipeline")
}
Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.
Nesne Yönelimli Programlama bölümündeki tüm dersler
1Go OOP Temelleri
Harici DosyalarGo Çalışma Alanı ve ModüllerPaketler ve İçe AktarmalarDışa Aktarılan ve Aktarılmayan İsimlerGo'da OOP'ye GirişSınıf Olarak Struct'larStruct'larda Metot TanımlamaPointer ve Değer AlıcılarStruct BaşlatmaYapıcı FonksiyonlarÖzet - Basit Hesap Makinesi4Arayüzler
Arayüzlere GirişÖrtük UygulamaSözleşme Olarak ArayüzBoş Arayüz (any)Tür OnaylamaTür SeçimiArayüz BileşimiStringer ve Error ArayüzleriÖzet - Şekil Hesaplayıcı7Kapsülleme
Dışa Aktarılan ve Aktarılmayan AlanlarPaket Seviyesinde KapsüllemeGetter ve Setter MetotlarıGo'da Bilgi GizlemeÖzet - Öğrenci Kayıtları10Generics (Go 1.18+)
Generics'e GirişTür ParametreleriTür KısıtlamalarıGeneric Struct'larGeneric Metotlar İçin Geçici ÇözümÖzet - Generic Koleksiyon13Tasarım Kalıpları Bölüm 1
Tasarım Kalıplarına GirişSingleton KalıbıFactory KalıbıAbstract Factory KalıbıObserver KalıbıStrategy Kalıbı2Türler ve Struct'lar: Derinlemesine İnceleme
Temel ve Bileşik TürlerÖzel Tür TanımlamalarıStruct EtiketleriAnonim Struct'larİç İçe Geçmiş Struct'larSıfır Değerler ve VarsayılanlarÖzet - Rehber5Kalıtım Yerine Kompozisyon
Go'da Neden Kalıtım YokStruct Gömme TemelleriMetot YükseltmeBirden Fazla Struct GömmeGömme vs AgregasyonGömülü Metotları GölgelemeÖzet - Çalışan Hiyerarşisi8Hata Yönetimi ve OOP
error ArayüzüÖzel Hata TürleriHata Sarmalama (fmt.Errorf)Sentinel Hatalarerrors.Is() ve errors.As()Panic, Defer ve RecoverÖzet - Dosya Ayrıştırıcı11Standart Kütüphane ve OOP
io.Reader ve io.Writersort.Interface Arayüzüfmt.Stringer ArayüzüStruct'lar ile encoding/jsonhttp.Handler ArayüzüÖzet - REST API Modelleri14Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit KalıbıDekoratör olarak Middleware3İşaretçiler ve Bellek
Go'da İşaretçi TemelleriStruct İşaretçileriDeğer ile Geçme ve Referans ile Geçmenew() FonksiyonuGo'da Garbage CollectionÖzet - Linked List Oluşturucu6Go'da Polimorfizm
Interface'ler ile PolimorfizmGo'da Duck TypingInterface Karşılama KurallarıPolimorfik KoleksiyonlarDependency InjectionÖzet - Payment Processor9Eşzamanlılık ve OOP
Goroutine TemelleriKanallar ve İletişimTamponlu ve Tamponsuz KanallarSelect İfadesisync.Mutex ve sync.RWMutexsync.WaitGroupThread-Safe Struct TasarımıÖzet - Worker Pool