Ce face net/http.Handler
Tot serverul HTTP din Go se sprijină pe o singură interfață: http.Handler, cu metoda ServeHTTP(w, r). Routerul (ServeMux), serverul de fișiere, TimeoutHandler și orice tip al tău cu această metodă sunt handler-e și se pot combina între ele.
http.HandlerFunc este un tip-funcție care are metoda ServeHTTP: http.HandlerFunc(f) transformă o funcție obișnuită într-un Handler. Așa se scriu și middleware-urile: funcții care primesc un handler și returnează altul, care face ceva înainte și după (jurnalizare, autentificare, măsurarea timpului).
Ce returnează
HandlerFunc(f) este o conversie de tip, nu un apel: rezultatul este un http.Handler.
Exemple
Exemplul 1: Tip propriu ca handler
Structura ține date (aici un contor), iar metoda ServeHTTP o face handler.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"sync/atomic"
)
type Contor struct {
vizite atomic.Int64
}
func (c *Contor) ServeHTTP(w http.ResponseWriter, r *http.Request) {
n := c.vizite.Add(1)
fmt.Fprintf(w, "Ești vizitatorul cu numărul %d", n)
}
func main() {
var h http.Handler = &Contor{}
for range 3 {
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest("GET", "/", nil))
fmt.Println(rec.Body.String())
}
}
Rezultat
Ești vizitatorul cu numărul 1
Ești vizitatorul cu numărul 2
Ești vizitatorul cu numărul 3
Exemplul 2: Middleware cu HandlerFunc
autentificare primește un handler și returnează altul, care verifică un antet înainte să-l apeleze pe cel original.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
)
func autentificare(urmator http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("X-Cheie") != "secret123" {
http.Error(w, "acces interzis", http.StatusUnauthorized)
return
}
urmator.ServeHTTP(w, r)
})
}
func main() {
panou := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Panoul de administrare")
})
h := autentificare(panou)
for _, cheie := range []string{"", "secret123"} {
req := httptest.NewRequest("GET", "/admin", nil)
if cheie != "" {
req.Header.Set("X-Cheie", cheie)
}
rec := httptest.NewRecorder()
h.ServeHTTP(rec, req)
fmt.Printf("cheie=%q -> %d %s\n", cheie, rec.Code, strings.TrimSpace(rec.Body.String()))
}
}
Rezultat
cheie="" -> 401 acces interzis
cheie="secret123" -> 200 Panoul de administrare
Greșeli frecvente
- Dai o funcție simplă unde se cere un
http.Handler(mux.Handle("/", f)): nu compilează. Foloseștemux.HandleFuncsauhttp.HandlerFunc(f). - Într-un middleware uiți
returndupă răspunsul de eroare și apelezi totuși handler-ul următor: clientul primește două răspunsuri amestecate.
Comenzi înrudite
net/http.HandleFunc: Înregistrează o rută în routerul global (DefaultServeMux): HandleFunc primește o funcție, Handle un Handler.net/http.NewServeMux: Creează un router (ServeMux) propriu care alege handler-ul după metoda și calea cererii; HandleFunc și Handle adaugă rute.net/http.ResponseWriter: Interfața prin care handler-ul construiește răspunsul: antete (Header), cod de stare (WriteHeader) și corp (Write).net/http.TimeoutHandler: Împachetează un handler cu o limită de timp: dacă nu termină în dt, clientul primește 503 cu mesajul dat.