El servidor web de la biblioteca estándar de Go es de los pocos que se pueden poner en producción tal cual. Y desde Go 1.22 su enrutador entiende métodos y parámetros en la ruta, que era lo único por lo que hacía falta una biblioteca externa.
También te puede interesar
El servidor
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hola desde Go")
})
mux.HandleFunc("GET /saludo/{nombre}", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hola, %s\n", r.PathValue("nombre"))
})
mux.HandleFunc("GET /api/estado", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{"ok": true, "version": 1})
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
}
log.Fatal(srv.ListenAndServe())
}cinco peticionesGET / 200 "Hola desde Go\n"
GET /saludo/Ana 200 "Hola, Ana\n"
GET /api/estado 200 "{\"ok\":true,\"version\":1}\n"
POST /api/estado 405 "Method Not Allowed\n"
GET /nada 404 "404 page not found\n"Las rutas
| Patrón | Qué recibe |
|---|---|
GET /{$} | solo la raíz, exacta |
GET /saludo/{nombre} | /saludo/Ana; el valor, con r.PathValue("nombre") |
GET /archivos/{ruta...} | todo lo que venga detrás, barras incluidas |
POST /api/usuarios | solo POST; otro método recibe un 405 |
Fíjate en el {$} de la primera ruta. Sin él, "/" en Go significa «la raíz y todo lo que empiece por ella», es decir, cualquier ruta. Era el comportamiento del ejemplo anterior de esta página, que respondía «Saludos, bienvenido» a cualquier dirección que escribieras, incluida una que no existía. Con {$}, lo que no está definido recibe su 404.
Y el 405 lo pone Go solo: la ruta existe para GET, y si alguien hace un POST, la respuesta es «método no permitido», con la cabecera Allow indicando cuáles sí.
Los tiempos de espera
http.ListenAndServe(":8080", nil), el atajo de siempre, no tiene ningún tiempo de espera. Un cliente que abre la conexión y envía las cabeceras letra a letra, muy despacio, la mantiene ocupada indefinidamente; con unos cuantos así, el servidor se queda sin conexiones. Es un ataque conocido con nombre propio, Slowloris. Por eso el ejemplo construye un http.Server con ReadHeaderTimeout, ReadTimeout y WriteTimeout: para algo expuesto a internet no son opcionales.Para una API de verdad faltarían dos piezas que también están en la biblioteca estándar: un apagado ordenado con srv.Shutdown(ctx), que deja terminar las peticiones en curso, y registrar cada petición. Pero ninguna exige añadir dependencias.
Más información en inglés: Create a web server with Golang.
Todo el código se compiló y se ejecutó con Go 1.27 en un contenedor limpio antes de publicar esta página; las salidas están copiadas de esa ejecución.