Go: Comunicación por HTTP

Go
Go
Actualizado: 14/02/2025

El paquete net/http de Go es una herramienta fundamental para desarrolladores que buscan crear servidores y clientes HTTP eficientes. Este paquete forma parte de la biblioteca estándar de Go y ofrece una amplia gama de funcionalidades para manejar comunicaciones HTTP de manera efectiva. A continuación, exploraremos en profundidad cómo utilizar el paquete net/http para construir aplicaciones web sólidas y escalables.

Introducción al paquete net/http

El paquete net/http proporciona las capacidades necesarias para:

  • Crear servidores HTTP.
  • Desarrollar clientes HTTP.
  • Manipular solicitudes y respuestas HTTP.
  • Implementar middleware y manejadores personalizados.

Al ser parte de la biblioteca estándar, no es necesario instalar dependencias externas para comenzar a utilizarlo.

Creación de un servidor HTTP básico

Para iniciar un servidor web en Go, se puede utilizar la función http.ListenAndServe. A continuación se muestra un ejemplo básico:

package main

import (
    "fmt"
    "net/http"
)

func manejadorSaludo(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "¡Bienvenido al servidor HTTP en Go!")
}

func main() {
    http.HandleFunc("/", manejadorSaludo)
    if err := http.ListenAndServe(":8080", nil); err != nil {
        fmt.Println("Error al iniciar el servidor:", err)
    }
}

Este código inicia un servidor que escucha en el puerto 8080 y responde con un mensaje de bienvenida a cualquier solicitud entrante.

Manejo de solicitudes y respuestas

El paquete net/http utiliza las estructuras http.Request y http.ResponseWriter para representar solicitudes y respuestas HTTP respectivamente.

Estructura de http.Request

La estructura http.Request contiene información sobre la solicitud entrante, como:

  • Método HTTP (GET, POST, etc.).
  • URL solicitada.
  • Encabezados y cookies.
  • Cuerpo de la solicitud.

Uso de http.ResponseWriter

El http.ResponseWriter permite enviar respuestas al cliente. Se puede escribir directamente en él para enviar el cuerpo de la respuesta y establecer encabezados HTTP.

func manejadorDatos(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"mensaje": "Datos procesados correctamente"}`))
}

Rutas y enrutamiento

Aunque el paquete net/http no incluye un enrutador sofisticado, permite asociar manejadores a rutas específicas usando http.HandleFunc o http.Handle.

http.HandleFunc("/usuarios", manejadorUsuarios)
http.HandleFunc("/productos", manejadorProductos)

Para aplicaciones más complejas, es común utilizar enrutadores externos, pero es posible implementar enrutamiento básico con las herramientas estándar.

Manejadores personalizados y la interfaz http.Handler

Un manejador en Go es cualquier objeto que implemente la interfaz http.Handler, es decir, que tenga un método ServeHTTP.

type miManejador struct{}

func (h *miManejador) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Este es un manejador personalizado utilizando http.Handler")
}

func main() {
    manejador := &miManejador{}
    http.Handle("/personalizado", manejador)
    http.ListenAndServe(":8080", nil)
}

Este enfoque permite mayor flexibilidad y reutilización de código en aplicaciones de gran escala.

Middleware y composición de manejadores

Los middleware son componentes que se ejecutan antes o después de los manejadores principales, permitiendo agregar funcionalidades transversales como autenticación, registro de solicitudes o manejo de errores.

func registroMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Println("Solicitud recibida:", r.URL.Path)
        next.ServeHTTP(w, r)
    })
}

func manejadorPrincipal(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Respuesta del manejador principal")
}

func main() {
    http.Handle("/", registroMiddleware(http.HandlerFunc(manejadorPrincipal)))
    http.ListenAndServe(":8080", nil)
}

Servir archivos estáticos

Para servir archivos estáticos, como archivos HTML, CSS o imágenes, se utiliza http.FileServer.

func main() {
    fs := http.FileServer(http.Dir("./public"))
    http.Handle("/static/", http.StripPrefix("/static/", fs))
    http.ListenAndServe(":8080", nil)
}

Este código sirve los archivos ubicados en el directorio public bajo la ruta /static/.

Manejando parámetros de consulta y formularios

Parámetros de consulta

Los parámetros de consulta en la URL pueden obtenerse a través de r.URL.Query().

func manejadorConsulta(w http.ResponseWriter, r *http.Request) {
    valores := r.URL.Query()
    id := valores.Get("id")
    fmt.Fprintf(w, "El ID proporcionado es: %s", id)
}

Procesamiento de formularios

Para procesar datos enviados mediante formularios (método POST), se utiliza r.ParseForm().

func manejadorFormulario(w http.ResponseWriter, r *http.Request) {
    if err := r.ParseForm(); err != nil {
        http.Error(w, "Error al procesar el formulario", http.StatusBadRequest)
        return
    }
    nombre := r.FormValue("nombre")
    correo := r.FormValue("correo")
    fmt.Fprintf(w, "Nombre: %s, Correo: %s", nombre, correo)
}

Subida de archivos al servidor

El manejo de archivos enviados desde formularios se realiza mediante r.FormFile.

func manejadorSubida(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Método no permitido", http.StatusMethodNotAllowed)
        return
    }
    archivo, encabezado, err := r.FormFile("archivo")
    if err != nil {
        http.Error(w, "Error al obtener el archivo", http.StatusBadRequest)
        return
    }
    defer archivo.Close()

    // Procesar el archivo (por ejemplo, guardarlo en el servidor)
    rutaDestino := "./uploads/" + encabezado.Filename
    ficheroDestino, err := os.Create(rutaDestino)
    if err != nil {
        http.Error(w, "Error al guardar el archivo", http.StatusInternalServerError)
        return
    }
    defer ficheroDestino.Close()
    io.Copy(ficheroDestino, archivo)

    fmt.Fprintf(w, "Archivo subido exitosamente: %s", encabezado.Filename)
}

Implementación de clientes HTTP

Además de crear servidores, el paquete net/http permite realizar solicitudes HTTP como cliente.

func realizarSolicitud() {
    resp, err := http.Get("https://api.ejemplo.com/datos")
    if err != nil {
        fmt.Println("Error en la solicitud:", err)
        return
    }
    defer resp.Body.Close()

    cuerpo, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("Error al leer la respuesta:", err)
        return
    }

    fmt.Println("Respuesta del servidor:", string(cuerpo))
}

Para solicitudes más complejas, se puede utilizar http.Client y construir solicitudes con http.NewRequest.

Configuración de tiempo de espera y transporte

Es posible establecer configuraciones personalizadas en el cliente HTTP, como tiempos de espera y proxies.

func clientePersonalizado() {
    transport := &http.Transport{
        Proxy: http.ProxyFromEnvironment,
    }
    client := &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
    }

    req, err := http.NewRequest("GET", "https://api.ejemplo.com/datos", nil)
    if err != nil {
        fmt.Println("Error al crear la solicitud:", err)
        return
    }

    resp, err := client.Do(req)
    if err != nil {
        fmt.Println("Error en la solicitud:", err)
        return
    }
    defer resp.Body.Close()
    // Procesar la respuesta
}

Uso de contexto para cancelación y plazos

El uso del paquete context permite manejar cancelaciones y plazos de tiempo en solicitudes.

func solicitudConContexto() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, "GET", "https://api.ejemplo.com/lento", nil)
    if err != nil {
        fmt.Println("Error al crear la solicitud:", err)
        return
    }

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        fmt.Println("Error en la solicitud:", err)
        return
    }
    defer resp.Body.Close()
    // Procesar la respuesta
}

Implementación de HTTPS y manejo de certificados

Para servir contenido sobre HTTPS, se utilizan las funciones ListenAndServeTLS y se proporcionan los certificados SSL.

func main() {
    http.HandleFunc("/", manejadorSeguro)
    if err := http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", nil); err != nil {
        fmt.Println("Error al iniciar el servidor HTTPS:", err)
    }
}

Es importante gestionar correctamente los certificados y asegurar las conexiones para proteger la información transmitida.

Mejoras de rendimiento con HTTP/2

El soporte para HTTP/2 en Go es automático cuando se sirve sobre TLS usando ListenAndServeTLS. HTTP/2 mejora el rendimiento mediante multiplexación de solicitudes y reducción de latencia.

Consejos de seguridad en aplicaciones web

Al desarrollar aplicaciones web, es crucial seguir prácticas de seguridad para proteger el servicio y a los usuarios.

Validación y saneamiento de entradas

Siempre validar y sanear los datos recibidos de los usuarios para prevenir inyecciones y otros ataques.

import "html"

func manejadorSeguro(w http.ResponseWriter, r *http.Request) {
    entrada := r.FormValue("entrada")
    entradaSegura := html.EscapeString(entrada)
    fmt.Fprintf(w, "Entrada segura: %s", entradaSegura)
}

Uso de encabezados de seguridad

Establecer encabezados que indiquen a los navegadores cómo manejar el contenido.

func seguridadMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Strict-Transport-Se

Lecciones de este módulo

Explora todas las lecciones disponibles en Comunicación por HTTP

Explora más sobre Go

Descubre más recursos de Go

Alan Sastre - Autor del curso

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Go es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.