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

Publicado por

En esta entrada vamos a crear un editor simple para nuestras wiki. Utilizaremos un nuevo paquete que nos proporciona Golang para trabajar con etiquetas HTML.

Comenzando…

Ya no es necesario utilizar el paquete fmt para imprimir en consola, puedes eliminarlo si lo deseas; en mi caso, lo seguiré utilizando para imprimir un par de líneas de ayuda durante la ejecución del servidor pero queda a tu consideración.

Haremos uso del paquete html/template para crear plantillas de etiquetas HTML en archivos separados de nuestro código fuente. Los paquetes que importaremos quedarán como a continuación:

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

Vamos a necesitar dos nuevos manejadores de eventos: uno mostrará un área de texto para editar wikis, el otro nos permitirá guardar los datos del área de texto. El primero se podrá acceder por medio de la ruta “/edit/”, mientras que el segundo a través de“/save/”. En esta entrada nos centraremos en el editor, en la siguiente entrada agregaremos la función de guardar los cambios.

Nuestra función principal lucirá así:

func main(){
  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)
  http.ListenAndServe(":8080", nil)
}

Comentamos el llamado al manejador guardar para evitar errores. En la segunda parte vamos a quitar las diagonales y a desarrollar la función manejadorGuardar(). La primera plantilla HTML que necesitamos crear se llamará edit.html. Creamos el archivo y agregamos las siguientes líneas:

<h1>Editando: {{.Titulo}}</h1>

<form action="/save/{{.Titulo}}" method="POST">
<div><textarea name="body" rows="20" cols="80">{{printf "%s" .Cuerpo}}</textarea></div>
<div><input type="submit" value="Guardar"></div>
</form>

Las directivas de las plantillas son encerradas entre dos llaves (esa información provendrá de nuestras wikis). La instrucción printf “%s” .Cuerpo llama al cuerpo de la página (p.Cuerpo) como una cadena (string) en lugar de una cadena de bytes, similar a llamar a fmt.Printf(). Las demás son etiquetas HTML de uso general.

Ahora creamos el cuerpo de la función manejadorEditar():

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}
  }
  t, _ := template.ParseFiles("edit.html")
  t.Execute(w, p)
}

La función va a leer el contenido de edit.html y regresar un *template.Template. El método t.Execute() ejecuta la plantilla y escribe el HTML generado a http.ResponseWriter.

El paquete html/template nos ayuda a garantizar que se genera un HTML correcto y seguro; por ejemplo, automáticamente reemplaza algunos signos como el mayor que (>) por &gt; para garantizar que los datos del usuario no corrompan el HTML (quienes hayan usado WordPress alguna vez, entenderán este problema de reemplazo de caracteres), el paquete nos evita algunos problemas con la representación de caracteres.

En seguida, vamos a modificar el manejadorMostrarPagina() que hicimos en entradas anteriores para que haga uso de un archivo HTML como el editor (no quedaría muy bien que solo algunos los usaran y otros no):

func manejadorMostrarPagina(w http.ResponseWriter, r *http.Request){
  titulo := r.URL.Path[len("/view/"):]
  p, _ := cargarPagina(titulo)
  t, _ := template.ParseFiles(titulo + ".html")
  t.Execute(w, p)
}

Creamos el archivo view.html correspondiente:

<h1>{{.Titulo}}</h1>
<p>[<a href="/edit/{{.Titulo}}">edit</a>]</p>
<div>{{printf "%s" .Cuerpo}}</div>

¿Notaste que utilizamos instrucciones similares en ambos manejadores? Movamos las instrucciones duplicadas a una función llamada cargarPlantilla().

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

Modifiquemos los manejadores para que hagan uso de nuestra nueva función:

func manejadorMostrarPagina(w http.ResponseWriter, r *http.Request){
  titulo := r.URL.Path[len("/view/"):]
  p, _ := cargarPagina(titulo)
  cargarPlantilla(w, "view", p)
}

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)
}

Servidor funcional

El código del servidor completo quedaría de la siguiente forma:

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, _ := cargarPagina(titulo)
  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)
}

Compila y ejecuta el servidor como lo hemos hecho anteriormente:

go build servidor_wikis.go
./servidor_wikis

Para verificar que funciona tu servidor, accede a la siguiente dirección:

http://localhost:8080/view/Ejemplo

Deberá de aparecer un enlace para editar tu wiki (si intentas guardar los cambios con el botón “Guardar” que aparecerá, ocurrirá un error ya que hasta la segunda parte agregaremos el código de esa funcionalidad).

Finalizando…

En esta entrada aprendiste a utilizar plantillas HTML y a crear un editor simple de wikis. En la siguiente entrega te enseñaré a agregar la funcionalidad de guardar los cambios que hagas en tus wikis. Hasta la próxima. See ya!

Deja una respuesta

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