Go
Tutorial Go: API REST con net/http
Aprende a implementar APIs REST en Go usando net/http. Configura rutas, gestiona peticiones y utiliza JSON sin frameworks, optimizando tu aplicación web.
Aprende Go GRATIS y certifícateConfigurar un API REST JSON sin framework
Para configurar un API REST en Go sin emplear un framework externo, se utiliza el paquete estándar net/http
, que proporciona todas las herramientas necesarias para manejar peticiones HTTP. La clave está en definir handlers que respondan a las rutas específicas de nuestro API.
Primero, se debe crear un nuevo ServerMux utilizando http.NewServeMux()
. Este objeto actúa como un enrutador que asigna las rutas a sus respectivos handlers. El uso de NewServeMux
permite aprovechar características avanzadas, como el routing basado en métodos y el soporte de comodines en patrones de URL, introducido en versiones recientes de Go.
mux := http.NewServeMux()
Para manejar peticiones de distintas rutas, se utiliza HandleFunc
o Handle
, donde puedes definir rutas tanto estáticas como dinámicas. Con el routing basado en métodos, se puede especificar directamente el método HTTP en el patrón de la URL, mejorando la seguridad y precisión del diseño del API.
mux.HandleFunc("POST /items", createItemHandler)
mux.HandleFunc("GET /items", itemsHandler)
mux.HandleFunc("GET /items/{id}", itemHandler)
mux.HandleFunc("GET /items/{id}/details/{details...}", itemDetailsHandler)
Este ejemplo muestra cómo definir un manejador para crear un ítem, otro para recuperar un ítem específico y un tercer manejador que utiliza un comodín de múltiples segmentos para obtener detalles adicionales del ítem. Este enfoque permite que los handlers respondan únicamente a los métodos HTTP previstos, reduciendo el riesgo de manejar peticiones no deseadas.
Finalmente, se inicia el servidor HTTP vinculando el ServerMux configurado al puerto deseado. Esto se realiza con http.ListenAndServe
, que comienza a escuchar y servir las peticiones entrantes.
http.ListenAndServe(":8080", mux)
Este enfoque permite implementar un API REST JSON, aprovechando las capacidades del paquete estándar de Go sin la necesidad de frameworks adicionales.
package main
import (
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("POST /items", createItemHandler)
mux.HandleFunc("GET /items", itemsHandler)
mux.HandleFunc("GET /items/{id}", itemHandler)
mux.HandleFunc("GET /items/{id}/details/{details...}", itemDetailsHandler)
http.ListenAndServe(":8080", mux)
}
Recibir peticiones GET
En el desarrollo de un API REST utilizando Go, una de las operaciones más comunes es recibir peticiones GET para obtener recursos. La biblioteca estándar net/http
proporciona un marco robusto para manejar estas peticiones. Para gestionar las peticiones GET, se utiliza el ServerMux
configurado previamente, que permite definir rutas específicas y asociarlas a funciones handler.
En una aplicación típica, se define un handler que procesa las peticiones GET. Este handler es una función que recibe un http.ResponseWriter
y un *http.Request
como argumentos. El ResponseWriter
se utiliza para enviar respuestas HTTP al cliente, mientras que el objeto Request
contiene todos los detalles de la petición entrante.
Obtener todos los ítems
Para mostrar todos los ítems, se puede definir un handler adicional que responda a la ruta GET /items
y devuelva la lista completa en formato JSON:
type Item struct {
ID string `json:"id"`
Name string `json:"name"`
Features []string `json:"features"`
}
var items = []Item{
{ID: "1", Name: "Ítem 1", Features: []string{"Feature A", "Feature B"}},
{ID: "2", Name: "Ítem 2", Features: []string{"Feature C", "Feature D"}},
}
func itemsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(items)
}
En este ejemplo, el handler itemsHandler
simplemente codifica y devuelve el arreglo items
completo en formato JSON. Esta es una operación común en APIs REST para listar todos los recursos disponibles de un tipo específico.
Recuperar un ítem específico
Además de listar todos los ítems, se puede recuperar un ítem específico utilizando su identificador único a partir de una ruta GET /items/{id}
:
func itemHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
id := r.URL.PathValue("id")
for _, item := range items {
if item.ID == id {
json.NewEncoder(w).Encode(item)
return
}
}
http.Error(w, "Ítem no encontrado", http.StatusNotFound)
}
En este handler, se busca el ítem correspondiente en el arreglo items
usando el identificador id
extraído de la URL. Si se encuentra, se envía la representación JSON del ítem al cliente. Si no se encuentra, se devuelve un error 404 Not Found
.
Manejo de rutas con número indefinido de segmentos
Para ilustrar cómo manejar un número indefinido de segmentos en una ruta, se puede definir una ruta adicional que permita acceder a detalles específicos del ítem utilizando múltiples segmentos en la URL. Por ejemplo, la ruta /items/{id}/details/{details...}
puede capturar múltiples segmentos adicionales que representan diferentes aspectos o categorías de detalles del ítem.
mux.HandleFunc("GET /items/{id}/details/{details...}", itemDetailsHandler)
Definimos el handler correspondiente que procesará estos segmentos adicionales:
func itemDetailsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
id := r.PathValue("id")
detailsPath := r.PathValue("details")
// Buscar el ítem por ID
var foundItem *Item
for _, item := range items {
if item.ID == id {
foundItem = &item
break
}
}
if foundItem == nil {
http.Error(w, "Ítem no encontrado", http.StatusNotFound)
return
}
// Procesar los detalles adicionales
// Por ejemplo, dividir los detallesPath en segmentos
// Supongamos que detallesPath = "featureA/subfeature1"
detailsSegments := splitPath(detailsPath)
// Crear una respuesta que incluya los detalles solicitados
response := map[string]interface{}{
"id": foundItem.ID,
"name": foundItem.Name,
"features": foundItem.Features,
"details": detailsSegments,
}
json.NewEncoder(w).Encode(response)
}
// Función auxiliar para dividir el path en segmentos
func splitPath(path string) []string {
var segments []string
current := ""
for _, char := range path {
if char == '/' {
if current != "" {
segments = append(segments, current)
current = ""
}
} else {
current += string(char)
}
}
if current != "" {
segments = append(segments, current)
}
return segments
}
En este handler itemDetailsHandler
, se captura el parámetro id
y el parámetro details
que puede contener múltiples segmentos separados por barras (/
). La función auxiliar splitPath
divide el segmento detailsPath
en partes individuales para procesarlos según sea necesario. En este ejemplo, simplemente se incluyen en la respuesta para ilustrar cómo se pueden manejar múltiples segmentos.
Uso del ejemplo de parámetros indefinidos:
Supongamos que se realiza una petición GET a la siguiente URL:
GET http://localhost:8080/items/1/details/featureA/subfeature1
El handler itemDetailsHandler
extraerá:
id
:"1"
details
:"featureA/subfeature1"
Después de procesar, la respuesta JSON podría verse así:
{
"id": "1",
"name": "Ítem 1",
"features": ["Feature A", "Feature B"],
"details": ["featureA", "subfeature1"]
}
Recibir peticiones POST
En el desarrollo de APIs REST en Go, las peticiones POST son esenciales para crear recursos en el servidor. Utilizando el paquete estándar net/http
, es posible manejar estas peticiones. La clave para procesar peticiones POST es definir un handler adecuado que pueda interpretar el cuerpo de la petición y realizar las operaciones necesarias.
Para comenzar, se configura el ServerMux, que actúa como enrutador, mapeando las rutas a sus respectivos handlers. El routing basado en métodos permite asociar directamente el método HTTP a la ruta, garantizando que el handler responda únicamente a peticiones POST.
mux.HandleFunc("POST /items", createItemHandler)
El handler para una petición POST es una función que recibe un http.ResponseWriter
y un *http.Request
. El objeto Request
contiene el cuerpo de la petición, al que se puede acceder y leer para obtener los datos enviados por el cliente. Generalmente, estos datos se envían en formato JSON y deben ser decodificados en una estructura Go.
func createItemHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var item Item
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&item)
if err != nil {
http.Error(w, "Solicitud incorrecta", http.StatusBadRequest)
return
}
// Asignar un ID sencillo basado en la longitud del arreglo
item.ID = fmt.Sprintf("%d", len(items)+1)
// Agregar el nuevo ítem al arreglo
items = append(items, item)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(item)
}
En este ejemplo, el handler createItemHandler
utiliza un json.Decoder
para deserializar el cuerpo de la petición en una estructura Item
. Después de decodificar, asigna un ID sencillo al ítem basado en la longitud actual del arreglo items
y lo agrega al arreglo. Finalmente, responde al cliente con un código de estado 201 Created
y la representación JSON del ítem creado.
Realizar peticiones HTTP PUT y PATCH
El manejo de peticiones PUT y PATCH en una API REST con Go es esencial para permitir la modificación de recursos existentes. Utilizando el paquete net/http
, podemos implementar estas operaciones. Las peticiones PUT generalmente se utilizan para reemplazar un recurso completo, mientras que PATCH se enfoca en modificar partes específicas del recurso.
Para comenzar, se configura un ServerMux que mapea las rutas a sus respectivos handlers, especificando el método HTTP directamente en el patrón de la URL. Esto asegura que cada handler responda únicamente al método esperado, aumentando la seguridad de la API.
mux.HandleFunc("PUT /items/{id}", updateItemHandler)
mux.HandleFunc("PATCH /items/{id}", patchItemHandler)
Manejo de peticiones PUT
El handler para una petición PUT debe ser capaz de manejar el reemplazo completo del recurso. Se espera que el cliente envíe la representación completa del recurso en el cuerpo de la petición, que debe ser decodificada en una estructura Go.
func updateItemHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var updatedItem Item
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&updatedItem)
if err != nil {
http.Error(w, "Solicitud incorrecta", http.StatusBadRequest)
return
}
id := r.PathValue("id")
updatedItem.ID = id
// Buscar y reemplazar el ítem en el arreglo
for i, item := range items {
if item.ID == id {
items[i] = updatedItem
json.NewEncoder(w).Encode(updatedItem)
return
}
}
http.Error(w, "Ítem no encontrado", http.StatusNotFound)
}
En este ejemplo, el handler updateItemHandler
decodifica el cuerpo de la petición en una estructura Item
. Luego, asigna el ID extraído de la URL al ítem actualizado y busca el ítem correspondiente en el arreglo items
. Si lo encuentra, reemplaza el ítem existente con el nuevo y responde con la representación JSON del ítem actualizado. Si no se encuentra el ítem, responde con un error 404 Not Found
.
Manejo de peticiones PATCH
Para las peticiones PATCH, el enfoque es similar, pero se centra en actualizar solo los campos especificados. Esto requiere lógica adicional para fusionar las actualizaciones con el recurso existente.
func patchItemHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var updates map[string]interface{}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&updates)
if err != nil {
http.Error(w, "Solicitud incorrecta", http.StatusBadRequest)
return
}
id := r.PathValue("id")
for i, item := range items {
if item.ID == id {
// Aplicar actualizaciones
if name, ok := updates["name"].(string); ok {
items[i].Name = name
}
if features, ok := updates["features"].([]interface{}); ok {
var newFeatures []string
for _, feature := range features {
if f, ok := feature.(string); ok {
newFeatures = append(newFeatures, f)
}
}
items[i].Features = newFeatures
}
// Agregar más campos según sea necesario
json.NewEncoder(w).Encode(items[i])
return
}
}
http.Error(w, "Ítem no encontrado", http.StatusNotFound)
}
En patchItemHandler
, se decodifica el cuerpo de la petición en un mapa de actualizaciones parciales. Luego, busca el ítem correspondiente en el arreglo items
y aplica las actualizaciones especificadas. En este ejemplo, se actualizan los campos name
y features
si están presentes en la solicitud. Finalmente, responde con la representación JSON del ítem parcialmente actualizado. Si no se encuentra el ítem, responde con un error 404 Not Found
.
Realizar peticiones HTTP DELETE
El manejo de peticiones HTTP DELETE en un API REST desarrollado con Go es crucial para permitir la eliminación de recursos. Utilizando el paquete estándar net/http
, podemos implementar estas operaciones, aprovechando las capacidades avanzadas de enrutamiento de ServerMux
.
Para empezar, se debe configurar un ServerMux que asocie las rutas a sus respectivos handlers, especificando el método HTTP directamente en el patrón de la URL. Esto garantiza que cada handler responda únicamente al método DELETE esperado, lo que incrementa la seguridad del API.
mux.HandleFunc("DELETE /items/{id}", deleteItemHandler)
El handler para una petición DELETE es una función que recibe un http.ResponseWriter
y un *http.Request
. En este contexto, el handler debe ser capaz de interpretar el identificador del recurso que se desea eliminar, el cual se extrae de la ruta usando la capacidad de comodines de ServerMux
.
func deleteItemHandler(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
for i, item := range items {
if item.ID == id {
// Eliminar el ítem del arreglo
items = append(items[:i], items[i+1:]...)
w.WriteHeader(http.StatusNoContent)
return
}
}
http.Error(w, "Ítem no encontrado", http.StatusNotFound)
}
En este ejemplo, el handler deleteItemHandler
utiliza r.PathValue("id")
para obtener el parámetro dinámico id
de la URL, que identifica el recurso a eliminar. Recorre el arreglo items
para encontrar el ítem correspondiente y lo elimina utilizando la función append
para rebanar el arreglo. Finalmente, responde al cliente con un código de estado 204 No Content
para indicar que la eliminación fue exitosa y que no hay contenido adicional que devolver. Si no se encuentra el ítem, responde con un error 404 Not Found
.
Ejercicios de esta lección API REST con net/http
Evalúa tus conocimientos de esta lección API REST con net/http con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.
Composición de structs en lugar de herencia
Estructuras de control
Arrays y slices
Control de flujo y estructuras de bucle
Métodos con receptores por valor y por puntero
Generics
Crear e invocar funciones
Operadores y expresiones
Polimorfismo a través de Interfaces
Manejo explícito de errores
Estructuras structs
Tipos de datos, variables y constantes
Introducción a Go
Punteros y referencias
Instalación Go primer programa
Errores personalizados y trazabilidad
Estructuras de datos Mapas
Cadenas de texto y manipulación
Todas las lecciones de Go
Accede a todas las lecciones de Go y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.
Introducción A Go
Introducción Y Entorno
Instalación Y Primer Programa De Go
Introducción Y Entorno
Tipos De Datos, Variables Y Constantes
Sintaxis
Operadores Y Expresiones
Sintaxis
Cadenas De Texto Y Manipulación
Sintaxis
Estructuras De Control
Sintaxis
Control De Flujo Y Estructuras De Bucle
Sintaxis
Funciones
Sintaxis
Arrays Y Slices
Estructuras De Datos
Mapas
Estructuras De Datos
Punteros Y Referencias
Estructuras De Datos
Estructuras Structs
Programación Orientada A Objetos
Métodos Con Receptores Por Valor Y Por Puntero
Programación Orientada A Objetos
Polimorfismo A Través De Interfaces
Programación Orientada A Objetos
Composición De Structs En Lugar De Herencia
Programación Orientada A Objetos
Generics
Programación Orientada A Objetos
Manejo Explícito De Errores
Manejo De Errores Y Excepciones
Errores Personalizados Y Trazabilidad
Manejo De Errores Y Excepciones
Métodos Http Con Net/http
Comunicación Por Http
Api Rest Con Net/http
Comunicación Por Http
En esta lección
Objetivos de aprendizaje de esta lección
- Configurar un API REST en Go sin frameworks.
- Manejar rutas HTTP utilizando
ServerMux
. - Implementar solicitudes HTTP GET, POST, PUT, PATCH y DELETE.
- Gestionar rutas dinámicas y múltiples segmentos.
- Procesar y responder en formato JSON.
- Asegurar APIs utilizando enrutamiento basado en métodos.