00
:
00
:
00
:
00
•Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

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.