serve_json.go en Go
Una API JSON con el mux estándar: rutas, middleware, apagado ordenado.
// Command serve_json corre una pequeña API JSON con la biblioteca estándar.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/http"
"sync"
"time"
)
type tour struct {
Slug string `json:"slug"`
Title string `json:"title"`
Steps int `json:"steps"`
}
type store struct {
mu sync.RWMutex
tours map[string]tour
}
func (s *store) get(slug string) (tour, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
found, ok := s.tours[slug]
return found, ok
}
func writeJSON(w http.ResponseWriter, status int, body any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
_ = json.NewEncoder(w).Encode(body)
}
func routes(s *store) http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("GET /healthz", func(w http.ResponseWriter,
r *http.Request) {
writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
})
mux.HandleFunc("GET /tours/{slug}", func(w http.ResponseWriter,
r *http.Request) {
found, ok := s.get(r.PathValue("slug"))
if !ok {
writeJSON(w, http.StatusNotFound,
map[string]string{"error": "no such tour"})
return
}
writeJSON(w, http.StatusOK, found)
})
return withLogging(mux)
}
func withLogging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
started := time.Now()
next.ServeHTTP(w, r)
slog.Info("request", "method", r.Method, "path", r.URL.Path,
"took", time.Since(started).Round(time.Microsecond))
})
}
func main() {
data := &store{tours: map[string]tour{
"basics": {Slug: "basics", Title: "Language basics", Steps: 30},
}}
server := &http.Server{
Addr: "127.0.0.1:8099",
Handler: routes(data),
ReadHeaderTimeout: 5 * time.Second,
}
go func() {
slog.Info("listening", "addr", server.Addr)
if err := server.ListenAndServe(); err != nil &&
!errors.Is(err, http.ErrServerClosed) {
slog.Error("serve", "err", err)
}
}()
time.Sleep(50 * time.Millisecond) // sustituto de una señal
shutdownCtx, cancel := context.WithTimeout(context.Background(),
5*time.Second)
defer cancel()
if err := server.Shutdown(shutdownCtx); err != nil {
fmt.Println("forced shutdown:", err)
}
slog.Info("stopped cleanly")
}
Cómo funciona
- El mux de 1.22 maneja el método y la variable de ruta.
- El middleware envuelve el mux entero, y así cada ruta queda en el log.
- El apagado espera las peticiones en vuelo, con su propio plazo.
Palabras clave y builtins usados aquí
anybooldeferfuncgoifintmapreturnstringstructtype
El intento, en números
- Líneas
- 97
- Caracteres a escribir
- 2147
- Tokens
- 543
- Ritmo de tres estrellas
- 115 tpm
Al ritmo de tres estrellas de 115 tokens por minuto, este intento toma unos 283 segundos.
Paso 1 de 1 en Bis; paso 26 de 26 en Biblioteca estándar y estructura del proyecto.