En aquest tema, aprendrem què és el middleware en el context del desenvolupament web amb Go, com crear i utilitzar middleware, i veurem alguns exemples pràctics. El middleware és una part essencial de moltes aplicacions web, ja que permet la manipulació de sol·licituds i respostes de manera modular i reutilitzable.
Què és el Middleware?
El middleware és un component que s'interposa en el flux de sol·licituds i respostes d'una aplicació web. Permet processar les sol·licituds abans que arribin als controladors finals i manipular les respostes abans que es retornin als clients. Alguns usos comuns del middleware inclouen:
- Autenticació i autorització
- Registre de sol·licituds
- Gestió d'errors
- Compressió de respostes
- Caching
Creació de Middleware en Go
En Go, el middleware es pot implementar com una funció que rep un http.Handler
i retorna un altre http.Handler
. Aquesta funció pot realitzar operacions abans i després de cridar al següent http.Handler
en la cadena.
Exemple Bàsic de Middleware
A continuació, es mostra un exemple bàsic de middleware que registra les sol·licituds entrants:
package main import ( "fmt" "log" "net/http" "time" ) // Middleware de registre func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() log.Printf("Iniciant sol·licitud %s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) log.Printf("Sol·licitud %s %s completada en %s", r.Method, r.URL.Path, time.Since(start)) }) } func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hola, món!") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", helloHandler) // Aplicar el middleware loggedMux := loggingMiddleware(mux) log.Println("Servidor escoltant en el port 8080") http.ListenAndServe(":8080", loggedMux) }
Explicació del Codi
- Funció
loggingMiddleware
: Aquesta funció rep unhttp.Handler
(next
) i retorna un nouhttp.Handler
que registra la sol·licitud abans i després de cridar anext.ServeHTTP(w, r)
. - Funció
helloHandler
: Un controlador simple que respon amb "Hola, món!". - Funció
main
: Es crea unServeMux
per gestionar les rutes, s'aplica el middleware de registre i es comença a escoltar en el port 8080.
Middleware Multiples
És comú utilitzar múltiples middleware en una aplicació. A continuació, es mostra com es poden encadenar diversos middleware:
package main import ( "fmt" "log" "net/http" "time" ) // Middleware de registre func loggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() log.Printf("Iniciant sol·licitud %s %s", r.Method, r.URL.Path) next.ServeHTTP(w, r) log.Printf("Sol·licitud %s %s completada en %s", r.Method, r.URL.Path, time.Since(start)) }) } // Middleware d'autenticació func authMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Authorization") != "Bearer token" { http.Error(w, "No autoritzat", http.StatusUnauthorized) return } next.ServeHTTP(w, r) }) } func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hola, món!") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", helloHandler) // Aplicar múltiples middleware handler := loggingMiddleware(authMiddleware(mux)) log.Println("Servidor escoltant en el port 8080") http.ListenAndServe(":8080", handler) }
Explicació del Codi
- Funció
authMiddleware
: Aquest middleware comprova si la sol·licitud té un encapçalament d'autorització vàlid. Si no és així, retorna un error 401. - Encadenament de Middleware: En la funció
main
, els middleware es poden encadenar aplicant-los successivament.
Exercicis Pràctics
Exercici 1: Middleware de Compressió
Implementa un middleware que comprimeixi les respostes utilitzant gzip
.
Exercici 2: Middleware de Caching
Implementa un middleware que emmagatzemi en memòria cau les respostes per a sol·licituds GET.
Solucions
Solució Exercici 1
package main import ( "compress/gzip" "fmt" "log" "net/http" "strings" ) // Middleware de compressió func gzipMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { next.ServeHTTP(w, r) return } w.Header().Set("Content-Encoding", "gzip") gz := gzip.NewWriter(w) defer gz.Close() gzrw := gzipResponseWriter{Writer: gz, ResponseWriter: w} next.ServeHTTP(gzrw, r) }) } type gzipResponseWriter struct { http.ResponseWriter Writer *gzip.Writer } func (w gzipResponseWriter) Write(b []byte) (int, error) { return w.Writer.Write(b) } func helloHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "Hola, món!") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", helloHandler) // Aplicar el middleware de compressió handler := gzipMiddleware(mux) log.Println("Servidor escoltant en el port 8080") http.ListenAndServe(":8080", handler) }
Solució Exercici 2
package main import ( "fmt" "log" "net/http" "sync" "time" ) // Middleware de caching func cacheMiddleware(next http.Handler) http.Handler { cache := make(map[string]string) var mu sync.Mutex return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet { next.ServeHTTP(w, r) return } mu.Lock() if response, found := cache[r.URL.Path]; found { mu.Unlock() fmt.Fprintln(w, response) return } mu.Unlock() rw := &responseWriter{ResponseWriter: w} next.ServeHTTP(rw, r) mu.Lock() cache[r.URL.Path] = rw.body mu.Unlock() }) } type responseWriter struct { http.ResponseWriter body string } func (rw *responseWriter) Write(b []byte) (int, error) { rw.body = string(b) return rw.ResponseWriter.Write(b) } func helloHandler(w http.ResponseWriter, r *http.Request) { time.Sleep(2 * time.Second) // Simular una operació costosa fmt.Fprintln(w, "Hola, món!") } func main() { mux := http.NewServeMux() mux.HandleFunc("/", helloHandler) // Aplicar el middleware de caching handler := cacheMiddleware(mux) log.Println("Servidor escoltant en el port 8080") http.ListenAndServe(":8080", handler) }
Conclusió
El middleware és una eina poderosa per modularitzar i reutilitzar la lògica comuna en les aplicacions web. En aquest tema, hem après què és el middleware, com crear-lo i utilitzar-lo, i hem vist exemples pràctics de middleware de registre, autenticació, compressió i caching. Amb aquests coneixements, estàs preparat per implementar middleware personalitzat en les teves aplicacions Go.
Curs de Programació en Go
Mòdul 1: Introducció a Go
- Introducció a Go
- Configuració de l'Entorn Go
- El Teu Primer Programa en Go
- Sintaxi i Estructura Bàsiques
Mòdul 2: Conceptes Bàsics
Mòdul 3: Estructures de Dades Avançades
Mòdul 4: Gestió d'Errors
Mòdul 5: Concurrència
Mòdul 6: Temes Avançats
Mòdul 7: Desenvolupament Web amb Go
Mòdul 8: Treballant amb Bases de Dades
Mòdul 9: Desplegament i Manteniment
- Construcció i Desplegament d'Aplicacions Go
- Registre
- Monitorització i Optimització del Rendiment
- Millors Pràctiques de Seguretat