05. Go web: ¿cómo crear un editor de páginas con Go? Parte 2 de 2

Publicado por

En la entrada anterior creamos un pequeño editor para páginas wiki pero nos faltó agregar el código de la función que guardaría los cambios hechos; en esta entrada completaremos esa funcionalidad.

Comenzamos

Primeramente, vamos a quitar las diagonales del llamado al manejadorGuardar() en nuestra función principal(main()) del servidor de la entrada anterior.

En seguida, creamos la función que se encargará de guardar wikis:

//Manejador para guardar wikis
func manejadorGuardar(w http.ResponseWriter, r * http.Request) {
  titulo := r.URL.Path[len("/save/"):]
  cuerpo := r.FormValue("body")
  p := &Pagina{Titulo: titulo, Cuerpo: []byte(cuerpo)}
  fmt.Println("Guardando " + titulo + ".txt...")
  p.guardar()
  http.Redirect(w, r, "/view/" + titulo, http.StatusFound)
}

Además de extraer el nombre de la wiki que intentamos guardar, almacenamos el único componente“body”(cuerpo) del formulario(form) o área de texto que se mostraba para editar el texto de las páginas. A continuación creamos un registro de tipo Pagina con los miembros Titulo y Cuerpo correspondientes extraídos de la petición y el formulario respectivamente.

Acceso a páginas que no existen

Si intentas acceder a una página que no existe(por ejemplo, http://localhost:8080/view/Prueba) se cargará una página completamente en blanco en nuestro navegador debido a que el servidor simplemente ignora el error que regresa la función cargarPagina() y procede a utilizar datos en blanco. Algo razonable es que se te redireccione al editor para crear una página nueva cuando intentes acceder a una que no existe (en entradas posteriores trabajaremos con otras soluciones). Para lograr eso agregaremos unas cuantas líneas de código extra a la función manejadorMostrarPagina() para que quede de la siguiente forma:

//Manejador de peticiones
func manejadorMostrarPagina(w http.ResponseWriter, r *http.Request){
  titulo := r.URL.Path[len("/view/"):]
  p, err := cargarPagina(titulo)
  if err != nil {
    http.Redirect(w, r, "/edit/" + titulo, http.StatusFound)
    fmt.Println("La página solicitada no existía. Llamando al editor...")
    return
  }
  cargarPlantilla(w, "view", p)
}

Cuando el servidor no puede localizar la página que le pidió el cliente, almacena en la variable err la información del error (en este caso, si se imprimiera en consola dicha variable, obtendríamos: open ./view/Prueba.txt: no such file or directory). Al identificar que la variable err no está vacía, es decir, que algo falló, el servidor procede a llamar a la función http.Redirect() con una nueva URL como argumento y un código de estado http.StatusFound de 302 para “decirle” al navegador del cliente que el servidor le intenta redireccionar. A partir de ahí se hace llamado a la función manejadorEditar() puesto que es la encargada de atender las peticiones de edición“/edit/”.

Servidor funcional

El siguiente es el código fuente del servidor tal y como lo hemos programado hasta este punto:

package main
import (
  "fmt"
  "io/ioutil"
  "net/http"
  "html/template"
)

type Pagina struct{
  Titulo string
  Cuerpo []byte
}

func main(){
  //Creamos y guardamos una página para que el cliente la pida
  pag1 := &Pagina{Titulo: "Ejemplo", Cuerpo:[]byte(
    "¡Hola personita! Este es el cuerpo de tu página.")}
  pag1.guardar()

  http.HandleFunc("/view/", manejadorMostrarPagina)
  http.HandleFunc("/edit/", manejadorEditar)
  http.HandleFunc("/save/", manejadorGuardar)
  fmt.Println("El servidor se encuentra en ejecución.");
  http.ListenAndServe(":8080", nil)
}

//Método para guardar página
func ( p* Pagina ) guardar() error {
  nombre := p.Titulo + ".txt"
  return ioutil.WriteFile( "./view/" + nombre, p.Cuerpo, 0600)
}

//Método para cargar página
func cargarPagina( titulo string ) (*Pagina, error) {
  nombre_archivo := titulo + ".txt"
  fmt.Println("El cliente ha pedido: " + nombre_archivo)
  cuerpo, err := ioutil.ReadFile( "./view/" + nombre_archivo )
  if err != nil {
    return nil, err
  }
  return &Pagina{Titulo: titulo, Cuerpo: cuerpo}, nil
}

//Carga las plantillas HTML
func cargarPlantilla(w http.ResponseWriter, plantilla string, p *Pagina){
  t, _ := template.ParseFiles(plantilla + ".html")
  t.Execute(w, p)
}

//Manejador de peticiones
func manejadorMostrarPagina(w http.ResponseWriter, r *http.Request){
  titulo := r.URL.Path[len("/view/"):]
  p, err := cargarPagina(titulo)
  if err != nil {
    http.Redirect(w, r, "/edit/" + titulo, http.StatusFound)
    fmt.Println("La página solicitada no existía. Llamando al editor...")
    return
  }
  cargarPlantilla(w, "view", p)
}

//Manejador para editar wikis
func manejadorEditar(w http.ResponseWriter, r *http.Request){
  titulo := r.URL.Path[len("/edit/"):]
  p, err := cargarPagina(titulo)
  if err != nil{
    p = &Pagina{Titulo: titulo}
  }
  cargarPlantilla(w, "edit", p)
}

//Manejador para guardar wikis
func manejadorGuardar(w http.ResponseWriter, r * http.Request) {
  titulo := r.URL.Path[len("/save/"):]
  cuerpo := r.FormValue("body")
  p := &Pagina{Titulo: titulo, Cuerpo: []byte(cuerpo)}
  fmt.Println("Guardando " + titulo + ".txt...")
  p.guardar()
  http.Redirect(w, r, "/view/" + titulo, http.StatusFound)
}

Para verificar que funciona lo compilamos y ejecutamos como siempre:

go build servidor_wikis.go
./servidor_wikis

Y accedemos a la siguiente URL en la que aparecerá el editor puesto que la página solicitada no existe (ten cuidado de ingresar la URL correctamente):

http://localhost:8080/view/PaginaQueNoExiste

Finalizando…

Espero que esta entrada te haya servido y que hayas logrado completar el servidor con lo aprendido hasta aquí. En las siguientes entrada continuaremos haciendo más robusto nuestro servidor. Si tienes alguna duda, puedes dejarla en los comentarios y con gusto te responderé. Hasta la próxima. See ya!

3 comments

  1. practicamente soy el único que ha llegado hasta este punto desde hace un año. terminaré aprendiendo go con mis nietos a este paso jajajjaja. la verdad, resulta confuso seguir el hilo del proyecto. las funciones no se ven elementales como en la primera temporada

    1. Hola. De hecho tienes razón, no son tan elementales como en la primera pero esa es la intención. Con la primera serie de entradas aprendemos el lenguaje, ahora hacemos algo un poco mas específico para ilustrar un proyecto o una situación que podría pasar en la vida real.

Responder a Uriel Ruelas Cancelar respuesta

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