net/http.NewServeMux în Go

Creează un router (ServeMux) propriu care alege handler-ul după metoda și calea cererii; HandleFunc și Handle adaugă rute.

Sintaxă
func NewServeMux() *ServeMux
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
func (mux *ServeMux) Handle(pattern string, handler Handler)

// modele (Go 1.22): "[METODĂ ][HOST]/cale/{param}/{rest...}"

Ce face net/http.NewServeMux

Un http.ServeMux este un router: primește fiecare cerere, caută modelul care se potrivește cel mai bine cu calea și apelează handler-ul acelui model. http.NewServeMux() creează unul gol, iar mux.HandleFunc și mux.Handle adaugă rute, exact ca funcțiile globale. Routerul este și el un http.Handler, deci îl dai lui ListenAndServe sau lui http.Server.

Din Go 1.22 modelele sunt mai puternice: pot începe cu metoda ("POST /comenzi"), pot avea parametri ("/produse/{id}", citiți cu r.PathValue("id")), un parametru final care prinde restul căii ("/fisiere/{cale...}") și {$} pentru potrivire exactă. Când două modele se potrivesc, câștigă cel mai specific.

Dacă nicio rută nu se potrivește, routerul răspunde 404 Not Found; dacă se potrivește calea, dar nu și metoda, răspunde 405 Method Not Allowed cu antetul Allow.

Ce returnează

NewServeMux: un *http.ServeMux nou, gol.

Exemple

Exemplul 1: Un mic API pentru produse

package main

import (
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Bun venit în magazin")
	})
	mux.HandleFunc("GET /produse", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Lista produselor: cafea, ceai")
	})
	mux.HandleFunc("GET /produse/{id}", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "Detalii pentru produsul", r.PathValue("id"))
	})
	mux.HandleFunc("POST /produse", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusCreated)
		fmt.Fprintln(w, "Produs adăugat")
	})

	srv := httptest.NewServer(mux)
	defer srv.Close()

	cereri := [][2]string{{"GET", "/"}, {"GET", "/produse"}, {"GET", "/produse/17"},
		{"POST", "/produse"}, {"DELETE", "/produse"}, {"GET", "/cos"}}
	for _, c := range cereri {
		req, _ := http.NewRequest(c[0], srv.URL+c[1], nil)
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			fmt.Println(err)
			return
		}
		corp, _ := io.ReadAll(resp.Body)
		resp.Body.Close()
		fmt.Printf("%-6s %-11s %d %s\n", c[0], c[1], resp.StatusCode, strings.TrimSpace(string(corp)))
	}
}

Rezultat

GET    /           200 Bun venit în magazin
GET    /produse    200 Lista produselor: cafea, ceai
GET    /produse/17 200 Detalii pentru produsul 17
POST   /produse    201 Produs adăugat
DELETE /produse    405 Method Not Allowed
GET    /cos        404 404 page not found

Exemplul 2: Mux.Handle și testare fără rețea

httptest.NewRecorder ține minte răspunsul, iar mux.ServeHTTP tratează cererea direct, fără server și fără port.

package main

import (
	"fmt"
	"net/http"
	"net/http/httptest"
)

type Stare struct{ Versiune string }

func (s Stare) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "ok, versiunea %s", s.Versiune)
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("GET /stare", Stare{Versiune: "1.4.2"})
	mux.HandleFunc("/fisiere/{cale...}", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "fișierul cerut: %s", r.PathValue("cale"))
	})

	for _, url := range []string{"/stare", "/fisiere/2026/facturi/f1.pdf"} {
		rec := httptest.NewRecorder()
		mux.ServeHTTP(rec, httptest.NewRequest("GET", url, nil))
		fmt.Println(rec.Code, rec.Body.String())
	}
}

Rezultat

200 ok, versiunea 1.4.2
200 fișierul cerut: 2026/facturi/f1.pdf

Greșeli frecvente

  • Înregistrezi "/produse" și te aștepți să prindă și /produse/17. Fără / la final, modelul se potrivește doar cu calea exactă; folosește "/produse/" sau "/produse/{id}".
  • Scrii două spații sau alt separator între metodă și cale ("GET /x", "GET:/x"). Între metodă și cale trebuie exact un spațiu.
  • Uiți că un model cu metodă nu există în versiunile Go mai vechi de 1.22 (sau cu go mai mic în go.mod): "GET /x" este luat drept cale literală.

Comenzi înrudite

  • net/http.HandleFunc: Înregistrează o rută în routerul global (DefaultServeMux): HandleFunc primește o funcție, Handle un Handler.
  • net/http.Request.PathValue: Returnează valoarea unui parametru din calea rutei, de exemplu id din modelul „/produse/{id}”.
  • net/http.Handler: Interfața oricărui obiect care răspunde la cereri HTTP (metoda ServeHTTP); HandlerFunc transformă o funcție în Handler.
  • net/http.Server: Serverul HTTP configurabil: adresă, handler, limite de timp; ListenAndServe îl pornește, Shutdown îl oprește curat.