Saltar al contenido

Lección 10 de 20 · Lectura y práctica

Quiz: Pruebas de validación y manejo de errores

Tipo de lección: QuizConcepto claveEn APIs de alto rendimiento, la validación de datos y el manejo de errores no son solo cuestiones de seguridad, sino de estabilidad del sistema. Imagina un restaurante de lujo donde los.

Del concepto a la práctica

Taller de esta lección

Evalúa una orden de reposición con sku, quantity y priority. La validación de sintaxis y la regla de negocio producen fallos distintos: quantity:"2" no tiene el tipo esperado; quantity:0 tiene tipo correcto pero viola el mínimo.

Ejemplo resuelto

Para {"sku":"A-7","quantity":3,"priority":"normal"} se espera 201. Para {"sku":"A-7","quantity":0,"priority":"normal"} se espera 400 con el campo quantity. Para un SKU inexistente en una actualización, se espera 404.

Pruébalo

  1. Prepara tres cuerpos POST (válido, quantity cadena, quantity cero) y la actualización de SKU ausente.

  2. Ejecuta cada caso mediante httptest.

  3. Comprueba estado, campo de error y longitud del almacenamiento.

  4. Verifica que los fallos no dejan registros parciales.

Error frecuente: comprobar solo el código HTTP. Corrígelo validando también el JSON y el estado final de los datos.

Comprueba tu resultado

El caso válido debe crear una orden; los dos casos inválidos deben dejar la longitud sin cambios. La actualización de un SKU ausente debe ser 404, no 500.

Cuatro solicitudes y efectos precisos

Declara SKU A-7 existente y reglas del ejercicio: quantity entero1..1000,priority en low/normal/high. Inicia órdenes vacías. POST{"sku":"A-7","quantity":3,"priority":"normal"}→201,len1. POST con quantity:"2"→400 tipo,len1. POST con quantity:0→400 quantity,len1. PUT/reposiciones/ZZ-9 con cuerpo válido→404 SKU ausente,len1. No uses la palabra normal del fixture con un enum distinto como medium sin adaptar uno de ellos.

Separa los tres cuerpos POST y la actualización: el texto anterior parecía contar tres cuerpos aunque solo mostraba dos. No necesitan un buggy_handler.go externo: la matriz declara todas las entradas. Cada caso requiere status, JSON público, campo y longitud posterior. El ejercicio histórico usa422 para reglas semánticas; esta ampliación documenta400 para esas reglas, con400 para JSON roto en ambos. Ambas políticas pueden existir, pero no cambies arbitrariamente el estado dentro de una misma API.

Preguntas: string2 no es entero;0 es entero inválido; ausencia de SKU no es500; fallo no crea registro parcial. Nunca usa panic para validar datos ordinarios. httptest permite estas comprobaciones sin abrir servidor ni llamar proveedor.

Diagrama de decisión con ramas Sí y No y un bucle que procesa cada elemento de una lista.
CursaloMapa de práctica
Cómo avanza un programa

Una condición decide qué camino ejecuta el programa. Un bucle repite una operación mientras queden elementos, y termina cuando la lista se agota. Separar ambas ideas ayuda a seguir el flujo y comprobar casos límite.

Ver la infografía en tamaño completo ↗

Tipo de lección: Quiz

Concepto clave

En APIs de alto rendimiento, la validación de datos y el manejo de errores no son solo cuestiones de seguridad, sino de estabilidad del sistema. Imagina un restaurante de lujo donde los comensales envían pedidos: si no validas que un pedido de "filete" incluya el punto de cocción, el chef podría prepararlo incorrectamente y el cliente quedaría insatisfecho. En Go, validar datos antes de procesarlos evita que errores simples propaguen fallos en cascada.

La validación debe ocurrir lo más cerca posible del punto de entrada, idealmente en el handler HTTP. Esto sigue el principio de "fail fast": detectar problemas temprano reduce el uso innecesario de recursos. Un error de validación bien manejado devuelve una respuesta HTTP clara (como 400 Bad Request) con detalles específicos, mientras que un error no manejado puede causar un panic que derribe el microservicio.

Cómo funciona en la práctica

Veamos el flujo típico en un handler de gorilla/mux:

  1. Recibir la solicitud HTTP (POST/PUT) con datos JSON en el cuerpo
  2. Decodificar el JSON a una estructura Go usando json.Unmarshal
  3. Validar cada campo según reglas de negocio (requerido, formato, rangos)
  4. Si hay errores, construir una respuesta de error estructurada
  5. Si es válido, proceder con la lógica de negocio

La clave está en separar claramente la validación de entrada de la lógica del dominio. Esto permite reutilizar validaciones y hacer el código más testeable.

Código en acción

Antes: Validación básica sin estructura clara

func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
    var user User
    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    
    // Validación dispersa en el handler
    if user.Name == "" {
        http.Error(w, "Name is required", http.StatusBadRequest)
        return
    }
    if user.Age < 18 {
        http.Error(w, "Age must be 18+", http.StatusBadRequest)
        return
    }
    // ... más lógica
}

Después: Validación estructurada con funciones dedicadas

type UserRequest struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

func (u *UserRequest) Validate() []string {
    var errors []string
    if strings.TrimSpace(u.Name) == "" {
        errors = append(errors, "name is required")
    }
    if !isValidEmail(u.Email) {
        errors = append(errors, "email format invalid")
    }
    if u.Age < 18 || u.Age > 120 {
        errors = append(errors, "age must be between 18 and 120")
    }
    return errors
}

func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
    var req UserRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        respondWithError(w, http.StatusBadRequest, "Invalid request format")
        return
    }
    
    if validationErrors := req.Validate(); len(validationErrors) > 0 {
        respondWithValidationErrors(w, validationErrors)
        return
    }
    
    // Lógica de negocio con datos validados
    // ...
}

Errores comunes

  • Validar solo en el cliente: Nunca confíes en la validación del frontend. Siempre valida en el backend, donde controlas las reglas de negocio.
  • Devolver errores genéricos: "Error de validación" no ayuda al cliente. Especifica qué campo falló y por qué (ej: "email: formato inválido").
  • Panic por datos inválidos: Usar panic() para errores de validación es excesivo. Maneja los errores gracefulmente con respuestas HTTP apropiadas.
  • Validar después de procesar: Si validas datos después de iniciar operaciones costosas (como consultas a BD), desperdicias recursos. Valida primero.
  • Olvidar sanitización: Validar no es lo mismo que sanitizar. Un email puede tener formato válido pero contener SQL injection. Usa parámetros preparados en consultas.

Checklist de dominio

  1. ¿Validas TODOS los datos de entrada, incluyendo parámetros de query y headers cuando son críticos?
  2. ¿Tus respuestas de error incluyen mensajes específicos y códigos HTTP apropiados (400, 422, etc.)?
  3. ¿Separaste la lógica de validación de la lógica de negocio para facilitar testing?
  4. ¿Usas estructuras dedicadas para requests en lugar de estructuras de dominio para validación?
  5. ¿Implementaste límites de tamaño para request bodies para prevenir DoS?
  6. ¿Tus validaciones incluyen reglas de negocio (ej: "saldo mínimo: 10") además de formato?
  7. ¿Documentaste las reglas de validación para que otros desarrolladores las entiendan?

Refactorizar handler con validación estructurada

En este ejercicio, mejorarás un handler existente que tiene problemas de validación. Sigue estos pasos:

  1. Descarga el código base desde el repositorio del curso (archivo: buggy_handler.go)
  2. Analiza el handler ProcessOrderHandler que actualmente valida datos de forma inconsistente
  3. Crea una estructura OrderRequest con los campos: ProductID (string), Quantity (int), Priority (string)
  4. Implementa el método Validate() []string que verifique:
    • ProductID no vacío y máximo 50 caracteres
    • Quantity entre 1 y 1000
    • Priority solo acepte valores: "low", "medium", "high"
  5. Refactoriza el handler para usar la nueva estructura y método Validate
  6. Implementa la función respondWithValidationErrors que devuelva JSON con formato: {"errors": ["error1", "error2"]} y status 422
  7. Escribe al menos 2 tests unitarios para el método Validate

Entrega: Archivo Go refactorizado y archivo de tests.

Pistas
  • Usa strings.TrimSpace() para validar campos string no vacíos
  • Considera usar un map[string]bool para validar valores permitidos en Priority
  • En los tests, prueba casos borde como Quantity = 0 y Quantity = 1001

Evalua tu comprension

Completa el quiz interactivo de arriba para ganar XP.

Sigue con Go con Gorilla/Mux: CRUD, Validación y Microservicios

Esta lección forma parte de un curso de Cursalo. Revisa su programa y elige entre la compra individual o el acceso a toda la escuela.

Si ya tenías acceso a este curso, entra con tu cuenta: tus derechos y tu progreso se conservan. No necesitas comprarlo de nuevo.

01 / ELEGIR UNA RUTA

El próximo paso

Aprende IA mientras construyes algo que sí necesitas.

Empieza por el curso inicial o explora el catálogo para encontrar una práctica alineada con tu trabajo.