HTTP Server
Il pacchetto net/http
Go include un server HTTP completo e performante nella libreria standard attraverso il pacchetto net/http. Non servono framework esterni per creare server web robusti e pronti per la produzione. In Go 1.22+ il routing e stato migliorato con il supporto per metodi HTTP e parametri nel percorso.
http.HandleFunc e http.ListenAndServe
Il modo piu semplice per creare un server HTTP:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Benvenuto nel server Go!")
})
http.HandleFunc("/saluta", func(w http.ResponseWriter, r *http.Request) {
nome := r.URL.Query().Get("nome")
if nome == "" {
nome = "Mondo"
}
fmt.Fprintf(w, "Ciao, %s!", nome)
})
fmt.Println("Server in ascolto su :8080")
if err := http.ListenAndServe(":8080", nil); err != nil {
fmt.Println("Errore:", err)
}
}
Request e ResponseWriter
L’oggetto *http.Request contiene tutte le informazioni sulla richiesta, mentre http.ResponseWriter e l’interfaccia per scrivere la risposta:
package main
import (
"fmt"
"io"
"net/http"
)
func infoHandler(w http.ResponseWriter, r *http.Request) {
// Informazioni dalla Request
fmt.Fprintf(w, "Metodo: %s\n", r.Method)
fmt.Fprintf(w, "URL: %s\n", r.URL.Path)
fmt.Fprintf(w, "Host: %s\n", r.Host)
fmt.Fprintf(w, "User-Agent: %s\n", r.UserAgent())
fmt.Fprintf(w, "Content-Type: %s\n", r.Header.Get("Content-Type"))
// Query parameters
for chiave, valori := range r.URL.Query() {
fmt.Fprintf(w, "Query %s: %v\n", chiave, valori)
}
}
func bodyHandler(w http.ResponseWriter, r *http.Request) {
// Leggere il body della richiesta
body, err := io.ReadAll(r.Body)
defer r.Body.Close()
if err != nil {
http.Error(w, "Errore nella lettura del body", http.StatusBadRequest)
return
}
// Impostare header della risposta
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Custom-Header", "valore")
// Impostare il codice di stato
w.WriteHeader(http.StatusOK)
w.Write(body)
}
func main() {
http.HandleFunc("/info", infoHandler)
http.HandleFunc("/body", bodyHandler)
http.ListenAndServe(":8080", nil)
}
Routing avanzato (Go 1.22+)
A partire da Go 1.22, il mux standard supporta metodi HTTP e parametri nel percorso:
package main
import (
"fmt"
"net/http"
)
func main() {
mux := http.NewServeMux()
// Routing con metodo specifico
mux.HandleFunc("GET /utenti", listaUtenti)
mux.HandleFunc("POST /utenti", creaUtente)
// Parametri nel percorso con {nome}
mux.HandleFunc("GET /utenti/{id}", ottieniUtente)
mux.HandleFunc("DELETE /utenti/{id}", eliminaUtente)
// Percorso con wildcard
mux.HandleFunc("GET /file/{percorso...}", serviFile)
fmt.Println("Server in ascolto su :8080")
http.ListenAndServe(":8080", mux)
}
func listaUtenti(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Lista di tutti gli utenti")
}
func creaUtente(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Utente creato")
}
func ottieniUtente(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id") // Nuovo in Go 1.22
fmt.Fprintf(w, "Utente con ID: %s", id)
}
func eliminaUtente(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
fmt.Fprintf(w, "Utente %s eliminato", id)
}
func serviFile(w http.ResponseWriter, r *http.Request) {
percorso := r.PathValue("percorso")
fmt.Fprintf(w, "File richiesto: %s", percorso)
}
Pattern Middleware
Il middleware e una funzione che avvolge un handler per aggiungere funzionalita trasversali come logging, autenticazione o CORS:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
// Middleware di logging
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
inizio := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(inizio))
})
}
// Middleware di autenticazione
func autenticazione(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Non autorizzato", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
// Middleware CORS
func cors(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusOK)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Pagina principale")
})
mux.HandleFunc("GET /protetto", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Contenuto protetto")
})
// Composizione dei middleware
handler := logging(cors(autenticazione(mux)))
http.ListenAndServe(":8080", handler)
}
Servire file statici
Per servire file statici come HTML, CSS, JavaScript e immagini:
package main
import (
"net/http"
)
func main() {
mux := http.NewServeMux()
// Servire una directory di file statici
fs := http.FileServer(http.Dir("./static"))
mux.Handle("/static/", http.StripPrefix("/static/", fs))
// Servire un singolo file
mux.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "./static/favicon.ico")
})
http.ListenAndServe(":8080", mux)
}
Endpoint API JSON
Un esempio completo di API REST con endpoint JSON:
package main
import (
"encoding/json"
"fmt"
"net/http"
"sync"
)
type Libro struct {
ID string `json:"id"`
Titolo string `json:"titolo"`
Autore string `json:"autore"`
Anno int `json:"anno"`
}
type LibroStore struct {
mu sync.RWMutex
libri map[string]Libro
}
func NuovoStore() *LibroStore {
return &LibroStore{libri: make(map[string]Libro)}
}
func (s *LibroStore) listaLibri(w http.ResponseWriter, r *http.Request) {
s.mu.RLock()
defer s.mu.RUnlock()
lista := make([]Libro, 0, len(s.libri))
for _, l := range s.libri {
lista = append(lista, l)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(lista)
}
func (s *LibroStore) creaLibro(w http.ResponseWriter, r *http.Request) {
var libro Libro
if err := json.NewDecoder(r.Body).Decode(&libro); err != nil {
http.Error(w, `{"errore":"JSON non valido"}`, http.StatusBadRequest)
return
}
s.mu.Lock()
s.libri[libro.ID] = libro
s.mu.Unlock()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(libro)
}
func main() {
store := NuovoStore()
mux := http.NewServeMux()
mux.HandleFunc("GET /api/libri", store.listaLibri)
mux.HandleFunc("POST /api/libri", store.creaLibro)
fmt.Println("API in ascolto su :8080")
http.ListenAndServe(":8080", mux)
}
Conclusione
Il pacchetto net/http di Go offre tutto il necessario per creare server web completi e performanti. Con Go 1.22+ il routing integrato supporta metodi HTTP e parametri nel percorso, riducendo la necessita di router di terze parti. Il pattern middleware permette di comporre funzionalita trasversali in modo pulito, e la combinazione con encoding/json rende semplice creare API REST. Per applicazioni di produzione, considerate anche la configurazione dei timeout del server e l’uso di TLS con http.ListenAndServeTLS.