Go: Comunicación por HTTP
Golang y el paquete net/http te permiten enviar y recibir peticiones HTTP, también crear servicios API REST sin necesidad de frameworks.
Aprende Go GRATIS y certifícateEl 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 de Go
Lecciones de programación del módulo Comunicación por HTTP del curso de Go.
Ejercicios de programación en este módulo de Go
Evalúa tus conocimientos en Comunicación por HTTP con ejercicios de programación Comunicación por HTTP de tipo Test, Puzzle, Código y Proyecto con VSCode.