01. Go web: ¿cómo crear aplicaciones web con Go?

Publicado por

Gracias a la primera temporada de Golang, ahora ya eres capaz de crear fácilmente pequeños programas de uso general. Muy bien, creo que es momento de retomar la programación con Go pero en esta ocasión para un fin en específico: crear aplicaciones web.

¿Quieres saber para qué es bueno Go con ejemplos reales? Ha llegado el momento de saberlo. Al crear aplicaciones web pondrás a prueba, entre otras cosas, los paquetes de Go, su concurrencia, su velocidad y la facilidad para crear programas que, en otros lenguajes, serían muy complicados de desarrollar.

Nota: en esta entrada no comenzaremos de lleno con temas web. Primero aprenderemos a crear y respaldar registros (algo fundamental para poder continuar).

Toda la información aquí presentada está basada en la documentación oficial del lenguaje. Puedes consultar la fuente original dando clic aquí.

Conocimientos y requisitos previos

Para comprender estas entradas se requiere que tengas nociones básicas de programación y de tecnologías web. A cada momento asumo que tienes conocimientos básicos de comandos de terminal, si no es así, te recomiendo que revises estas entradas. Si no utilizas Linux o Mac, no te preocupes, cualquier cosa que se haga desde terminal la puedes hacer desde línea de comandos de Windows con pocos o ningún cambio.

Aunque resulte obvio, necesitas tener instalado Go en tu equipo y agregar la carpeta de sus binarios a tu PATH (puedes consultar más información al respecto aquí).

Creación de páginas

Vamos a crear páginas estilo wiki para ejemplificar un poco el uso de algunas funciones disponibles en los paquetes de Go.

Lo primero que haremos es, desde terminal (o el gestor de archivos de tu preferencia), crearemos un directorio llamado ‘gowiki’ y nos moveremos dentro de él.

mkdir gowiki
cd gowiki

Ahora creamos un archivo llamado ‘wiki.go’ dentro de la carpeta ‘gowiki’. Ábrelo y agrega el siguiente código:

package main
import (
  "fmt"
  "io/ioutil"
)

Nota: el paquete fmt, como ya expliqué anteriormente, nos proporciona funcionalidades de entradas y salidas con formato. En cambio, ioutil nos brinda funciones de entrada y salida para archivos, directorios, etc..

Registros de datos

Las wiki consisten en páginas interconectadas que tienen un título y un cuerpo (o contenido). Utilizaremos registros (struct) con dos miembro para representar esos campos:

//Los registros representan páginas
type Pagina struct{
  Titulo string
  Cuerpo []byte
}

Nota: la razón por la que usamos un slice (trozo) en lugar de un string para el cuerpo, será aclarada un poco más adelante.

Las páginas se almacenarán en la memoria principal durante la ejecución. Si queremos mantener una copia en disco necesitamos crear el método guardar(); sería poco práctico que al cerrar nuestro programa no haya un respaldo de nuestras páginas:

//Este método almacenará páginas en disco duro
func (p* Pagina) guardar() error{
  nombre := p.Titulo + ".txt"
  return ioutil.WriteFile(nombre, p.Cuerpo, 0600)
}

El método anterior puede ser llamado por registros tipo Pagina. No recibe parámetros y regresa un valor tipo error. En el cuerpo del método está codificado el algoritmo para almacenar el cuerpo de una página en un archivo de texto con el nombre del título que esta posee. La función WriteFile() escribe un slice a un archivo, por eso nuestro miembro Cuerpo de los registros Pagina también fue declarada con ese tipo (en lugar de string, como mencionaba anteriormente).

El método regresa un dato tipo error porque es, a su vez, el que retorna la función WriteFile(). Esto es muy útil para que sea procesado correctamente por nuestra aplicación si ocurre algo malo. Si la función escribe correctamente la información, regresará nil (el valor cero).

El octal 0600 de argumento del método es usado para indicar que el archivo creado tendrá permisos de lectura y escritura únicamente para el usuario actual. Si quieres leer más al respecto, puedes consultar el manual de Unix para más detalles.

Leer páginas

Si almacenamos páginas, es de suponer que también queremos leerlas. La siguiente función realiza ese trabajo:

func cargarPagina(titulo string) (*Pagina, error) {
  nombre_archivo = titulo + ".txt"
  cuerpo, err := ioutil.ReadFile(nombre_archivo)

  if err != nil {
    return nil, err
  }

  return &Pagina{Titulo: titulo, Cuerpo: cuerpo}, nil
}

Esta función recibe de parámetro el nombre de la página a cargar; lee al archivo correspondiente en disco y guarda la información leída junto con un código de error. Si ocurrió algo malo, regresa nil y el dato de lo que falló . Si todo se realizó correctamente, regresa el apuntador a una literal tipo Pagina construida con sus respectivos título y cuerpo en ese momento, además de también regresar nil para señalar que no hubo error.

Recordarás que en la entrada de funciones detallé que es posible que una función regrese dos valores en Golang. Es exactamente eso lo que hace la función ReadFile(); regresa los datos leídos y un código de error. Si aún no has validado los errores (o no quieres hacerlo), puedes usar el identificador en blanco (_) para desecharlos ya que todo lo que se intente guardar en él será eliminado, sin embargo, lo recomendable es que al menos valides si ocurrió un error (sin distinción de cuál fue) porque eso significa que no se pudo leer el archivo correctamente. Si la variable devuelta err no es igual nil, no se creará la página y la función regresará el código del error ocurrido. Más adelante puedes crear sistemas de manejo de errores más eficientes y robustos pero por el momento nos limitaremos a detectar un error para evitar que falle nuestra aplicación.

Hasta este punto ya tenemos una estructura de datos simple que almacena y carga páginas desde archivos. El siguiente ejemplo muestra todo lo visto en esta entrada en un único código funcional:

package main
import ("fmt"; "io/ioutil")

//Registros que guardan páginas
type Pagina struct{
  Titulo string
  Cuerpo []byte
}

func main(){
  //Creamos y guardamos una página en disco.
  pag1 := &Pagina{Titulo: "Prueba 1", Cuerpo: []byte("Este es el cuerpo")}
  pag1.guardar()

  //Cargamos página guardada
  pag2, _ := cargarPagina("Prueba 1")

  fmt.Println(string(pag2.Cuerpo))
}

//Método de registros Pagina, almacena el cuerpo
func ( p* Pagina ) guardar() error {
  nombre := p.Titulo + ".txt"
  return ioutil.WriteFile( nombre, p.Cuerpo, 0600)
}

//Función que carga una página desde disco
func cargarPagina( titulo string ) (*Pagina, error) {
  nombre_archivo := titulo + ".txt"
  cuerpo, err := ioutil.ReadFile( nombre_archivo )
  if err != nil {
    return nil, err
  }
  return &Pagina{Titulo: titulo, Cuerpo: cuerpo}, nil
}

Salida:
Este es el cuerpo

Finalizando…

Este es solamente el principio para aprender a programar aplicaciones web con Go. En la siguiente entrada revisaremos un poco de puertos y peticiones HTTP. Hasta la próxima, see ya!

13 comments

  1. me sale un error al crear la página. textualmente dice:
    CreateFile wiki.go : El sistema no puede hallar el archivo especificado.

    ¿a que se debe ese mensaje?

    1. Hola, Aquiles, gracias por leernos. Eso se debe a que Go no es capaz de leer el archivo especificado. Tal vez cambiaste el nombre del archivo en el código. Trata de ejecutar el código que compartí en esta entrada de nuevo.

    1. Claro, depende mucho de lo que quieres hacer, la herramienta/lenguaje correcto que puedes optar por usar, pero definitivamente puedes usar Go para eso.

      Gracias por leernos.

  2. felicidades uriel, comprendo tu forma de explicar , gracias por compartir conocimiento, estoy migrando a go. Me interesaría documentación en español de go, es muy escasa y en este momento de pandemia estoy dedicado a aprender, agradezco cualquier información que me puedas compartir

    1. Hola, depende del tipo de recurso que busques. Pero te recomiendo consultar la web oficial (https://golang.org/) si no te importa leer en inglés, igualmente hay muchos libros sobre Golang pero casi todos, de nuevo, solamente se encuentran en inglés.

      Gracias por leernos.

Deja una respuesta

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