DMarlon Studio

Lección 19 · 19 de 20

Manejadores HTTP y httptest · Go

Escriba un manejador HTTP en Go con net/http que responde en JSON y pruébelo con httptest, sin red ni servidor. Así trabaja el destino Web (Go).

Go · Manejadores HTTP y httptest en DMarlon Studio: el código del ejemplo y su salida en la terminal

Un servidor web en Go es, sobre todo, un manejador: una función con la firma func(w http.ResponseWriter, r *http.Request). r trae la petición (método, ruta, parámetros) y en w se escribe la respuesta. En DMarlon Studio, Go es el servidor del destino Web (Go): las páginas de la aplicación piden datos a manejadores como este.

priceHandler lee el parámetro con r.URL.Query().Get("sku") y lo busca en prices. El switch decide la respuesta: si el método no es GET, `http.Error` escribe el código 405 y un texto; si el producto no existe, 404. Si no se indica ningún código, la respuesta sale con 200.

En el caso bueno, primero se fijan las cabeceras con w.Header().Set("Content-Type", "application/json") (después de escribir el cuerpo ya no se pueden cambiar) y luego json.NewEncoder(w).Encode(...) escribe el JSON directamente en la respuesta. Al ser un mapa, las claves salen en orden alfabético: price antes que sku.

`http.HandlerFunc(priceHandler)` convierte la función en un http.Handler, la interfaz que usa todo net/http. HandlerFunc es un tipo de función con un método ServeHTTP que llama a la propia función: interfaces y funciones como valores trabajando juntas.

Para probarlo no hace falta red. `httptest.NewRequest` construye una petición y `httptest.NewRecorder` es un ResponseWriter que guarda lo que se escribe: rec.Code, las cabeceras y rec.Body. Para servirlo de verdad bastaría con http.ListenAndServe(":8080", handler).

Código · main.go

package mainimport (	"encoding/json"	"fmt"	"net/http"	"net/http/httptest")func priceHandler(w http.ResponseWriter, r *http.Request) { // GET /api/price?sku=...	prices := map[string]float64{"KB-01": 49.9, "MS-02": 19.5}	sku := r.URL.Query().Get("sku")	price, ok := prices[sku]	switch {	case r.Method != http.MethodGet:		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)	case !ok:		http.Error(w, "unknown sku", http.StatusNotFound)	default:		w.Header().Set("Content-Type", "application/json")		json.NewEncoder(w).Encode(map[string]any{"sku": sku, "price": price})	}}func main() {	handler := http.HandlerFunc(priceHandler) // the adapter that makes it an http.Handler	requests := [][2]string{{"GET", "/api/price?sku=KB-01"}, {"GET", "/api/price?sku=ZZ-09"},		{"POST", "/api/price?sku=KB-01"}, {"GET", "/api/price?sku=MS-02"}}	for _, req := range requests {		rec := httptest.NewRecorder() // records status, headers and body: no network		handler.ServeHTTP(rec, httptest.NewRequest(req[0], req[1], nil))		fmt.Printf("%-4s %-21s -> %d %s", req[0], req[1], rec.Code, rec.Body)	}}

Salida · go run main.go

GET  /api/price?sku=KB-01  -> 200 {"price":49.9,"sku":"KB-01"}
GET  /api/price?sku=ZZ-09  -> 404 unknown sku
POST /api/price?sku=KB-01  -> 405 method not allowed
GET  /api/price?sku=MS-02  -> 200 {"price":19.5,"sku":"MS-02"}

Para recordar

  • Un manejador recibe un http.ResponseWriter y un *http.Request.
  • http.Error escribe un código de error y un texto.
  • Fije las cabeceras antes de escribir el cuerpo.
  • http.HandlerFunc convierte una función en un http.Handler.
  • httptest prueba manejadores sin red ni servidor.

Ejercicio

Haga que una petición sin sku (/api/price) responda 400 con el texto missing sku usando http.StatusBadRequest, y añádala a requests para comprobarlo.