Petición POST, GET, PUT y DELETE en Go con net/http

Golang es un lenguaje utilizado del lado del servidor debido a su simplicidad de uso, potencia y velocidad.

Sin embargo, con Go también se pueden hacer peticiones HTTP. Es decir, Go también puede funcionar como cliente HTTP, no solo como servidor.

Como sabemos, hay 4 métodos básicos para las peticiones HTTP: POST para enviar datos, GET para obtener, PUT para actualizar y DELETE para eliminar.

Peticiones HTTP con Go y net http

En este post veremos ejemplos de cómo hacer estas 4 peticiones con Go usando el paquete net/http para lograr hacer una petición GET, POST, PUT y DELETE usando Golang.

También vamos a ver cómo escribir y enviar encabezados o el código de respuesta (200 para OK, 500 de error, 405 de no permito, etcétera)

El sitio web que vamos a usar para probar será httpbin.org, el cual sirve como espejo para probar que realmente estamos haciendo las peticiones y enviando datos.

Peticiones HTTP con Golang

Voy a explicar lo básico y más abajo dejaré ejemplos.

Para comenzar importa el paquete net/http; es lo único que necesitas para las conexiones HTTP, aunque si vas a codificar con JSON debes importar encoding/json.

Ya veremos en el código cuáles cosas hacen falta. Comenzamos creando un cliente HTTP:

clienteHttp := &http.Client{}

Y también creamos una petición:

peticion, err := http.NewRequest("GET", url, nil)

La petición recibe 3 argumentos: el tipo o verbo, la url y los datos. Si es GET o simplemente no queremos enviar datos pasamos nil.

A la petición le podemos agregar encabezados:

peticion.Header.Add("X-Hola-Mundo", "Ejemplo")

Para realizar la petición (ahora sí hacerla) invocamos al método Do del cliente HTTP:

respuesta, err := clienteHttp.Do(peticion)

En caso de que err sea nil debemos cerrar el cuerpo, podemos usar defer para cerrarlo al final:

defer respuesta.Body.Close()

Para acceder al cuerpo lo leemos con ioutil.ReadAll:

cuerpoRespuesta, err := ioutil.ReadAll(respuesta.Body)

Podemos convertirlo a cadena, pues ReadAll lo devuelve como bytes. Para convertirlo hacemos esto:

respuestaString := string(cuerpoRespuesta)

Si queremos acceder al código de respuesta lo hacemos a través de StatusCode:

log.Printf("Código de respuesta: %d", respuesta.StatusCode)

Los encabezados se encuentran en respuesta.Header en forma de mapa. No hay necesidad de parsear los valores por nosotros, pues ya existe el método Get:

contentType := respuesta.Header.Get("Content-Type")

Sobra decir que podemos escribir la respuesta en un archivo, decodificarla como JSON o ignorarla.

Ahora sí veamos el código de ejemplo.

Petición GET

El código es el siguiente:

/*
  Cliente HTTP en Go con net/http
  Ejemplo de petición HTTP Get en Golang

  @author parzibyte
*/
package main

import (
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	clienteHttp := &http.Client{}
	// Si quieres agregar parámetros a la URL simplemente haz una
	// concatenación :)
	url := "https://httpbin.org/get"
	peticion, err := http.NewRequest("GET", url, nil)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error creando petición: %v", err)

	}
	// Podemos agregar encabezados
	peticion.Header.Add("Content-Type", "application/json")
	peticion.Header.Add("X-Hola-Mundo", "Ejemplo")
	respuesta, err := clienteHttp.Do(peticion)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error haciendo petición: %v", err)
	}
	// No olvides cerrar el cuerpo al terminar
	defer respuesta.Body.Close()

	cuerpoRespuesta, err := ioutil.ReadAll(respuesta.Body)
	if err != nil {
		log.Fatalf("Error leyendo respuesta: %v", err)
	}

	respuestaString := string(cuerpoRespuesta)
	log.Printf("Código de respuesta: %d", respuesta.StatusCode)
	log.Printf("Encabezados: '%q'", respuesta.Header)
	contentType := respuesta.Header.Get("Content-Type")
	log.Printf("El tipo de contenido: '%s'", contentType)
	// Aquí puedes decodificar la respuesta si es un JSON, o convertirla a cadena
	log.Printf("Cuerpo de respuesta del servidor: '%s'", respuestaString)

}

No estamos enviando nada en el cuerpo, pues es una petición GET. Si se desea indicar parámetros en la URL simplemente se usa una concatenación.

Petición POST

El código queda como a continuación:

/*
  Cliente HTTP en Go con net/http
  Ejemplo de petición HTTP POST enviando datos JSON
  en Golang

  @author parzibyte
*/
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	clienteHttp := &http.Client{}
	// Si quieres agregar parámetros a la URL simplemente haz una
	// concatenación :)
	url := "https://httpbin.org/post"
	type Usuario struct {
		Nombre string
		Edad   int
	}
	usuario := Usuario{
		Nombre: "Luis",
		Edad:   22,
	}
	usuarioComoJson, err := json.Marshal(usuario)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error codificando usuario como JSON: %v", err)
	}

	peticion, err := http.NewRequest("POST", url, bytes.NewBuffer(usuarioComoJson))
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error creando petición: %v", err)
	}

	// Podemos agregar encabezados
	peticion.Header.Add("Content-Type", "application/json")
	peticion.Header.Add("X-Hola-Mundo", "Ejemplo")
	respuesta, err := clienteHttp.Do(peticion)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error haciendo petición: %v", err)
	}

	// No olvides cerrar el cuerpo al terminar
	defer respuesta.Body.Close()

	cuerpoRespuesta, err := ioutil.ReadAll(respuesta.Body)
	if err != nil {
		log.Fatalf("Error leyendo respuesta: %v", err)
	}

	// Aquí puedes decodificar la respuesta si es un JSON, o convertirla a cadena
	respuestaString := string(cuerpoRespuesta)
	log.Printf("Código de respuesta: %d", respuesta.StatusCode)
	log.Printf("Encabezados: '%q'", respuesta.Header)
	contentType := respuesta.Header.Get("Content-Type")
	log.Printf("El tipo de contenido: '%s'", contentType)
	log.Printf("Cuerpo de respuesta del servidor: '%s'", respuestaString)
}

Ahora sí enviamos el cuerpo, el cual es un búfer que contiene un dato JSON. Podríamos enviar el JSON directamente como cadena o codificar un struct.

Petición PUT

Es casi igual que POST, solo cambia el verbo y la URL para httpbin. Desconozco cuál es la verdadera diferencia pero el código para hacer una petición PUT es el siguiente:

/*
  Cliente HTTP en Go con net/http
  Ejemplo de petición HTTP PUT enviando datos JSON
  en Golang

  @author parzibyte
*/
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	clienteHttp := &http.Client{}
	// Si quieres agregar parámetros a la URL simplemente haz una
	// concatenación :)
	url := "https://httpbin.org/put"
	type Usuario struct {
		Nombre string
		Edad   int
	}
	usuario := Usuario{
		Nombre: "Luis",
		Edad:   22,
	}
	usuarioComoJson, err := json.Marshal(usuario)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error codificando usuario como JSON: %v", err)
	}

	peticion, err := http.NewRequest("PUT", url, bytes.NewBuffer(usuarioComoJson))
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error creando petición: %v", err)
	}

	// Podemos agregar encabezados
	peticion.Header.Add("Content-Type", "application/json")
	peticion.Header.Add("X-Hola-Mundo", "Ejemplo")
	respuesta, err := clienteHttp.Do(peticion)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error haciendo petición: %v", err)
	}

	// No olvides cerrar el cuerpo al terminar
	defer respuesta.Body.Close()

	cuerpoRespuesta, err := ioutil.ReadAll(respuesta.Body)
	if err != nil {
		log.Fatalf("Error leyendo respuesta: %v", err)
	}

	// Aquí puedes decodificar la respuesta si es un JSON, o convertirla a cadena
	respuestaString := string(cuerpoRespuesta)
	log.Printf("Código de respuesta: %d", respuesta.StatusCode)
	log.Printf("Encabezados: '%q'", respuesta.Header)
	contentType := respuesta.Header.Get("Content-Type")
	log.Printf("El tipo de contenido: '%s'", contentType)
	log.Printf("Cuerpo de respuesta del servidor: '%s'", respuestaString)
}

Petición DELETE

Aquí entra una discusión que no tiene que ver con Go y es que si al hacer una llamada DELETE se pueden pasar datos en el cuerpo.

En algunos lugares es posible, es decir, algunos servidores lo aceptan, otros no. Yo recomiendo no pasar nada en el cuerpo.

El siguiente código hace una petición HTTP DELETE:

/*
  Cliente HTTP en Go con net/http
  Ejemplo de petición HTTP DELETE enviando datos JSON
  en Golang
  Nota: recuerda que DELETE tal vez no debería llevar
  cuerpo de petición, sin embargo es posible. Personalmente
  no lo recomiendo, pero queda en ti

  @author parzibyte
*/
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
)

func main() {
	clienteHttp := &http.Client{}
	// Si quieres agregar parámetros a la URL simplemente haz una
	// concatenación :)
	url := "https://httpbin.org/delete"
	type Usuario struct {
		Nombre string
		Edad   int
	}
	usuario := Usuario{
		Nombre: "Luis",
		Edad:   22,
	}
	usuarioComoJson, err := json.Marshal(usuario)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error codificando usuario como JSON: %v", err)
	}

	peticion, err := http.NewRequest("DELETE", url, bytes.NewBuffer(usuarioComoJson))
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error creando petición: %v", err)
	}

	// Podemos agregar encabezados
	peticion.Header.Add("Content-Type", "application/json")
	peticion.Header.Add("X-Hola-Mundo", "Ejemplo")
	respuesta, err := clienteHttp.Do(peticion)
	if err != nil {
		// Maneja el error de acuerdo a tu situación
		log.Fatalf("Error haciendo petición: %v", err)
	}

	// No olvides cerrar el cuerpo al terminar
	defer respuesta.Body.Close()

	cuerpoRespuesta, err := ioutil.ReadAll(respuesta.Body)
	if err != nil {
		log.Fatalf("Error leyendo respuesta: %v", err)
	}

	// Aquí puedes decodificar la respuesta si es un JSON, o convertirla a cadena
	respuestaString := string(cuerpoRespuesta)
	log.Printf("Código de respuesta: %d", respuesta.StatusCode)
	log.Printf("Encabezados: '%q'", respuesta.Header)
	contentType := respuesta.Header.Get("Content-Type")
	log.Printf("El tipo de contenido: '%s'", contentType)
	log.Printf("Cuerpo de respuesta del servidor: '%s'", respuestaString)
}

Conclusión

Golang funciona como servidor y como cliente HTTP. Podemos hacer múltiples llamadas a otros servidores utilizando el protocolo HTTP y enviando datos.

Personalmente utilicé las peticiones en Go para probar mi servidor. Es decir, programé el servidor y después hice los tests automatizados para probar que el servidor funcionaba.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *