Middleware sebagai Decorator
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan GO di Coddy. Pelajaran 97 dari 107.
Middleware adalah salah satu penerapan pola Decorator yang paling praktis di Go. Dalam pengembangan web, fungsi middleware membungkus handler HTTP untuk menambahkan kebutuhan lintas fungsi seperti pencatatan log, autentikasi, atau pengukuran waktu tanpa mengubah handler aslinya.
Inti utamanya adalah middleware menerima handler dan mengembalikan handler baru dengan perilaku tambahan. Ini mengikuti pola Decorator secara tepat: antarmuka yang sama saat masuk dan keluar, dengan fungsionalitas yang ditingkatkan:
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)
}
}Setiap middleware membungkus handler berikutnya dalam rantai, lalu menambahkan perilakunya sebelum atau sesudah memanggil handler yang dibungkus. Kamu dapat menyusun beberapa middleware secara berurutan:
func MainHandler(request string) string {
return "Response to: " + request
}
handler := LoggingMiddleware(AuthMiddleware(MainHandler))
fmt.Println(handler("GET /users"))
// [LOG] [AUTH] Respons untuk: GET /usersUrutan pembungkusan menentukan urutan eksekusi: middleware terluar dijalankan terlebih dahulu. Pola ini banyak digunakan dalam framework web Go untuk pipeline pemrosesan permintaan, sehingga memudahkan penambahan atau penghapusan fungsionalitas tanpa menyentuh logika inti handler.
Tantangan
MudahMari buat pipeline pemrosesan request menggunakan pola Middleware sebagai Decorator! Kamu akan membuat sistem tempat fungsi middleware membungkus handler untuk menambahkan fungsionalitas seperti pencatatan waktu, validasi, dan pemformatan, semuanya tanpa mengubah logika inti handler.
Kamu akan mengatur kodemu dalam tiga file:
handler.go: Tentukan tipe handler dan handler dasar yang memproses request.Buat tipe
Handlersebagai fungsi yang menerima string request dan mengembalikan string respons:type Handler func(request string) stringImplementasikan fungsi
BaseHandleryang mengembalikanProcessed: [request]untuk setiap request yang diterimanya.middleware.go: Buat fungsi middleware yang membungkus handler dengan perilaku tambahan.Buat tiga fungsi middleware, yang masing-masing menerima
Handlerdan mengembalikanHandlerbaru:TimingMiddleware: membungkus respons dengan info waktu, lalu mengembalikan[TIMING] [response]ValidationMiddleware: menambahkan prefiks validasi, lalu mengembalikan[VALID] [response]UppercaseMiddleware: mengubah seluruh respons menjadi huruf kapital
Setiap middleware harus memanggil handler yang dibungkusnya dan menyempurnakan hasilnya.
main.go: Bangun pipeline pemrosesan dengan menyusun middleware.Baca jumlah lapisan middleware yang akan diterapkan. Lalu, untuk setiap lapisan, baca tipe middleware (
timing,validation, atauuppercase) dan bungkus handler saat ini dengan middleware tersebut. Terapkan middleware sesuai urutan pembacaannya: middleware pertama yang dibaca menjadi pembungkus terluar.Setelah membangun pipeline, baca string request, proses melalui handler yang telah kamu susun, lalu cetak hasilnya.
Input berikut akan diberikan:
- Baris 1: Jumlah lapisan middleware
- Baris berikutnya: Satu tipe middleware per baris (
timing,validation, atauuppercase) - Baris terakhir: Request yang akan diproses
Contohnya, untuk input:
2
timing
validation
GET /usersOutput-mu seharusnya:
[TIMING] [VALID] Processed: GET /usersUntuk input:
3
uppercase
timing
validation
POST /dataOutput-mu seharusnya:
[TIMING] [VALID] PROCESSED: POST /DATAUntuk input:
1
uppercase
hello worldOutput-mu seharusnya:
PROCESSED: HELLO WORLDUntuk input:
0
simple requestOutput-mu seharusnya:
Processed: simple requestPerhatikan bagaimana urutan pembungkusan middleware memengaruhi output: middleware pertama yang diterapkan (terluar) berjalan pertama dan menerima hasil akhir dari semua middleware di dalamnya. Middleware uppercase pada contoh kedua berjalan terakhir (paling dalam), sehingga mengubah respons dasar menjadi huruf kapital sebelum timing dan validation menambahkan prefiksnya!
Coba sendiri
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Baca jumlah lapisan middleware
nLine, _ := reader.ReadString('\n')
n, _ := strconv.Atoi(strings.TrimSpace(nLine))
// Baca jenis middleware
middlewareTypes := make([]string, n)
for i := 0; i < n; i++ {
line, _ := reader.ReadString('\n')
middlewareTypes[i] = strings.TrimSpace(line)
}
// Baca permintaan
request, _ := reader.ReadString('\n')
request = strings.TrimSpace(request)
// TODO: Mulai dengan BaseHandler
// TODO: Bangun pipeline dengan membungkus handler menggunakan middleware
// Terapkan middleware secara berurutan, middleware yang dibaca pertama menjadi pembungkus terluar
// Petunjuk: Lakukan perulangan pada middlewareTypes dalam urutan TERBALIK untuk mencapai ini
// Middleware yang diterapkan terakhir akan menjadi yang terluar (berjalan lebih dulu)
// TODO: Proses permintaan melalui handler yang telah disusun dan cetak hasilnya
fmt.Println("TODO: implement pipeline")
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP di Go
File EksternalWorkspace Go dan modulePaket dan ImporNama yang Diekspor dan Tidak DieksporPengantar OOP di Gostruct sebagai classMendefinisikan method pada structPointer receiver vs value receiverInisialisasi structFungsi KonstruktorUlasan: Kalkulator Sederhana4Interface
Pengenalan InterfaceImplementasi ImplisitInterface sebagai KontrakInterface Kosong (any)Type AssertionType SwitchKomposisi InterfaceInterface Stringer & ErrorUlasan: Kalkulator Bentuk7Enkapsulasi
Field yang Diekspor vs Tidak DieksporEnkapsulasi Tingkat PackageMethod Getter dan SetterPenyembunyian Informasi di GoRingkasan: Data Siswa10Generics (Go 1.18+)
Pengantar GenericsParameter TipeBatasan TipeStruct GenerikCara Alternatif untuk Method GenerikRingkasan Koleksi Generik2Mendalami Tipe dan Struct
Tipe Dasar dan KompositDefinisi Tipe KustomTag StructStruct AnonimStruct BersarangNilai Nol dan Nilai BawaanRangkuman: Buku Kontak5Komposisi daripada Pewarisan
Mengapa Go Tidak Memiliki PewarisanDasar Penyematan StructPromosi methodMenyematkan Beberapa StructPenyematan vs AgregasiMembayangi method yang DisematkanRangkuman: Hierarki Karyawan8Penanganan Error & OOP
Interface errorTipe Error KustomPembungkusan Error (fmt.Errorf)Error Sentinelerrors.Is() dan errors.As()Panic, Defer, dan RecoverRangkuman: Parser File11Pustaka Standar & OOP
io.Reader & io.Writersort.InterfaceAntarmuka fmt.Stringerencoding/json dengan StructAntarmuka http.HandlerRingkasan, Model REST API14Pola Desain Bagian 2
Pola CommandPola AdapterPola DecoratorPola Template MethodPola StatePola CompositeMiddleware sebagai Decorator3Pointer dan Memori
Dasar-Dasar Pointer di GoPointer ke StructPass by Value vs ReferenceFungsi new()Garbage Collection di GoRekap: Pembuat Linked List6Polimorfisme di Go
Polimorfisme melalui InterfaceDuck Typing di GoAturan Pemenuhan InterfaceKoleksi PolimorfikInjeksi DependensiRangkuman: Pemroses Pembayaran9Konkruensi & OOP
Dasar-dasar GoroutineChannel & KomunikasiChannel Ber-buffer vs Tanpa BufferPernyataan selectsync.Mutex & sync.RWMutexsync.WaitGroupDesain Struct yang Aman untuk ThreadRingkasan: Worker PoolLatihan sendiri: Compiler Go online