Goa fornisce una serie di middleware HTTP pronti all’uso che coprono i casi d’uso più comuni. Questi middleware sono stati testati e ottimizzati per funzionare al meglio con l’architettura di Goa.
func BasicAuth(username, password string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, pass, ok := r.BasicAuth()
if !ok || user != username || pass != password {
w.Header().Set("WWW-Authenticate", "Basic realm=Restricted")
http.Error(w, "Non autorizzato", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
}
func SecurityHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Imposta header di sicurezza comuni
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Content-Security-Policy", "default-src 'self'")
next.ServeHTTP(w, r)
})
}
Il middleware CORS gestisce le richieste Cross-Origin Resource Sharing:
func CORS(allowedOrigins []string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
origin := r.Header.Get("Origin")
// Verifica se l'origine è consentita
allowed := false
for _, o := range allowedOrigins {
if o == origin || o == "*" {
allowed = true
break
}
}
if allowed {
w.Header().Set("Access-Control-Allow-Origin", origin)
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
// Gestisci le richieste preflight
if r.Method == "OPTIONS" {
w.WriteHeader(http.StatusOK)
return
}
}
next.ServeHTTP(w, r)
})
}
}
Il middleware di compressione gestisce automaticamente la compressione delle risposte:
func Compression(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Verifica se il client supporta la compressione
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
// Crea un writer compresso
gz := gzip.NewWriter(w)
defer gz.Close()
// Sostituisci il ResponseWriter con uno che supporta la compressione
gzw := &gzipResponseWriter{
ResponseWriter: w,
Writer: gz,
}
// Imposta l'header Content-Encoding
w.Header().Set("Content-Encoding", "gzip")
next.ServeHTTP(gzw, r)
})
}
type gzipResponseWriter struct {
http.ResponseWriter
io.Writer
}
func (gzw *gzipResponseWriter) Write(data []byte) (int, error) {
return gzw.Writer.Write(data)
}
Il middleware di logging registra i dettagli delle richieste HTTP:
func RequestLogging(logger *log.Logger) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Crea un response writer che traccia lo status code
rw := &responseWriter{ResponseWriter: w}
// Processa la richiesta
next.ServeHTTP(rw, r)
// Registra i dettagli della richiesta
duration := time.Since(start)
logger.Printf(
"%s %s %s %d %s",
r.Method,
r.RequestURI,
r.RemoteAddr,
rw.status,
duration,
)
})
}
}
type responseWriter struct {
http.ResponseWriter
status int
}
func (rw *responseWriter) WriteHeader(code int) {
rw.status = code
rw.ResponseWriter.WriteHeader(code)
}
Il middleware di rate limiting protegge il servizio dal sovraccarico:
func RateLimit(requests int, window time.Duration) func(http.Handler) http.Handler {
limiter := rate.NewLimiter(rate.Every(window/time.Duration(requests)), requests)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !limiter.Allow() {
http.Error(w, "Troppe richieste", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
}
Il middleware di recupero gestisce i panic e previene i crash del server:
func Recovery(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer func() {
if err := recover(); err != nil {
// Registra lo stack trace
log.Printf("panic: %v\n%s", err, debug.Stack())
// Restituisci un errore 500
http.Error(w, "Errore interno del server", http.StatusInternalServerError)
}
}()
next.ServeHTTP(w, r)
})
}
Esempio di come utilizzare i middleware integrati insieme:
func main() {
// Crea il logger
logger := log.New(os.Stdout, "", log.LstdFlags)
// Crea il mux
mux := http.NewServeMux()
// Aggiungi gli handler
mux.Handle("/api/", apiHandler)
// Crea la catena di middleware
handler := Recovery(
SecurityHeaders(
RequestLogging(logger)(
CORS([]string{"*"})(
Compression(
RateLimit(100, time.Minute)(
mux,
),
),
),
),
),
)
// Avvia il server
log.Fatal(http.ListenAndServe(":8080", handler))
}
Do
Don’t
I middleware integrati di Goa forniscono:
Usa questi middleware come punto di partenza e personalizzali secondo le tue esigenze specifiche.