Introducción: Por qué es importante saber trabajar con HTTP en Go
\nGo (Golang) es un lenguaje creado para trabajar en red. Su biblioteca estándar net/http proporciona herramientas potentes y flexibles para crear tanto servidores como clientes. Sin embargo, muchos desarrolladores, especialmente aquellos que vienen de lenguajes de scripting, cometen errores típicos: olvidan cerrar el cuerpo de la respuesta, no establecen tiempos de espera o manejan incorrectamente los errores.
En este artículo, analizaremos la creación de un cliente HTTP en Go «de la A a la Z». Aprenderá cómo enviar correctamente solicitudes GET y POST, gestionar encabezados, configurar tiempos de espera y manejar errores. Todos los ejemplos de código están listos para usar.
\n\n1. Fundamentos: creación del cliente y primera solicitud GET
\nEn Go, el cliente HTTP es una estructura http.Client. Puede usar el cliente global http.DefaultClient o crear el suyo propio con configuraciones personalizadas. Comencemos con una solicitud GET simple.
package main\n\nimport (\n "fmt"\n "io"\n "net/http"\n "os"\n)\n\nfunc main() {\n // Usamos el cliente incorporado\n resp, err := http.Get("https://api.example.com/data")\n if err != nil {\n fmt.Printf("Error en la solicitud: %v\\n", err)\n os.Exit(1)\n }\n defer resp.Body.Close() // IMPORTANTE: ¡siempre cerrar el cuerpo de la respuesta!\n\n // Leemos el cuerpo de la respuesta\n body, err := io.ReadAll(resp.Body)\n if err != nil {\n fmt.Printf("Error al leer la respuesta: %v\\n", err)\n os.Exit(1)\n }\n\n fmt.Printf("Estado: %s\\n", resp.Status)\n fmt.Printf("Cuerpo: %s\\n", string(body))\n}\nPuntos clave:
\n- \n
- Siempre cierre
resp.Bodymediantedefer. De lo contrario, se producirá una fuga de recursos (las conexiones no volverán al grupo). \n - Verifique el estado de la respuesta (
resp.StatusCode). El código 200 no es el único exitoso (existen 201, 204, etc.). \n io.ReadAlllee todo el cuerpo en memoria. Para respuestas grandes, useio.Copyo procesamiento por flujo. \n
2. Cliente avanzado: tiempos de espera, encabezados y configuraciones personalizadas
\nEl cliente global no tiene tiempos de espera por defecto; esto puede provocar que el programa se cuelgue. Siempre cree su propio cliente con configuraciones.
\n2.1. Creación de un cliente con tiempo de espera
\nclient := &http.Client{\n Timeout: 10 * time.Second, // Tiempo de espera total para toda la solicitud\n}\n\nresp, err := client.Get("https://api.example.com/data")\nTambién puede configurar tiempos de espera en diferentes niveles a través de Transport:
transport := &http.Transport{\n DialContext: (&net.Dialer{\n Timeout: 5 * time.Second, // Tiempo de espera de conexión\n KeepAlive: 30 * time.Second,\n }).DialContext,\n TLSHandshakeTimeout: 5 * time.Second,\n ResponseHeaderTimeout: 5 * time.Second,\n}\n\nclient := &http.Client{\n Transport: transport,\n Timeout: 15 * time.Second,\n}\n\n2.2. Adición de encabezados y parámetros
\nPara solicitudes complejas, use http.NewRequest:
req, err := http.NewRequest("GET", "https://api.example.com/search", nil)\nif err != nil {\n log.Fatal(err)\n}\n\n// Agregamos encabezados\nreq.Header.Set("Authorization", "Bearer your-token")\nreq.Header.Set("Accept", "application/json")\n\n// Agregamos parámetros de consulta\nq := req.URL.Query()\nq.Add("query", "golang")\nq.Add("page", "1")\nreq.URL.RawQuery = q.Encode()\n\nresp, err := client.Do(req)\n\n3. Trabajo con POST y otros métodos (JSON, formularios)
\nLas solicitudes POST requieren el envío de un cuerpo. Consideremos dos formatos populares: JSON y formulario codificado en URL.
\n3.1. POST con JSON
\ntype RequestData struct {\n Name string `json:"name"`\n Email string `json:"email"`\n}\n\ndata := RequestData{\n Name: "Iván",\n Email: "ivan@example.com",\n}\n\njsonData, err := json.Marshal(da