09. Go web: crear y redireccionar a la página principal

Publicado por

En esta entrada vamos a realizar unos cambios para tener un poco más organizado nuestro servidor y que además pueda cumplir con otro requisito importante: redireccionar a una página principal.

Organización de plantillas

En las notas anteriores almacenamos las plantillas HTML en la misma carpeta que el código fuente del servidor. Para organizar un poco nuestros archivos, vamos a colocar todas las plantillas en su propia carpeta dentro del proyecto. Comenzamos creando la carpeta tmpl en la raíz del servidor. Movemos los archivos .html que creamos anteriormente (view.html y edit.html) dentro de dicha carpeta.

Abrimos el código fuente del servidor y agregamos el prefijo “tmpl/” a las plantillas de la siguiente línea:

var plantillas = template.Must(template.ParseFiles("edit.html", "view.html"))//Antes

Debería quedar de este modo:

var plantillas = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html"))

Redireccionar a la página principal

Nuestro servidor necesita tener una página principal desde la cual permita a sus visitantes moverse entre las demás páginas que hemos creado. Por ahora te voy a mostrar cómo crear dicha página y en las siguientes vamos a añadir el código para las funcionalidades que tendrá.

Primero, vamos a crear la plantilla de la página principal dentro de la carpeta tmpl. Yo la nombraré front.html. La plantilla contendrá las siguientes líneas:

<h1>Has sido redireccionado a: {{.Titulo}}</h1>
{{printf "%s" .Cuerpo}}

Por el momento solamente mostrará dos mensajes: un aviso de que ha sido redireccionado y un mensaje adicional que tú desees.

En seguida, modificamos la variable plantillas para que acepte “tmpl/front.html”:

var plantillas = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html", "tmpl/front.html"))

La página principal se mostrará cuando el usuario intente acceder a la raíz del servidor “/”, por tanto, modificamos la regex de rutas de acceso para que considere “/” como parte de las rutas permitidas:

var regex_ruta = regexp.MustCompile("^/|((edit|save|view)/([a-zA-Z0-9]+))?$")

Opcional: puedes crear una variable pagina_principal (o el nombre que desees darle) que almacene el nombre del archivo que tendrá la información de la página principal. Esto es buena práctica de programación porque disminuyes la cantidad de literales en tu código y, si quisieras cambiar la página a la que te redireccione el servidor, solamente modificarías la cadena de la variable. En mi caso quedaría así:

var pagina_principal = "Principal" //también puede llamarse "Home", por ejemplo

Ahora, creamos un manejador para la página principal. Yo lo llamaré manejadorRaiz():

func manejadorRaiz(w http.ResponseWriter, r *http.Request, titulo string) {
  p, err := cargarPagina(pagina_principal)
  if err != nil {
    http.Redirect(w, r, "edit/" + pagina_principal, http.StatusFound)
    fmt.Println("Error")
    return
  }
  cargarPlantilla(w, "front", p)
}

El manejador hace lo mismo que los otros tres que ya tenemos hechos:

  • Carga la página solicitada
  • Si no existe, crear una nueva
  • Si ya existe, la devuelve al cliente con el formato de la plantilla correspondiente.

Como puedes darte cuenta, con la función cargarPlantilla() cargamos la plantilla front.html que recién creamos. Cuando intentemos acceder a “/”, el servidor abrirá la página “/view/Principal” en su lugar.

Como modificamos la expresión regular (regex), necesitamos actualizar la función dameTitulo() puesto que cambiaron los submatches que devuelve FindStringSubmatch, ahora el título ya no estará en la posición 2:

func dameTitulo(w http.ResponseWriter, r *http.Request) (string, error) {
  peticion := regex_ruta.FindStringSubmatch(r.URL.Path)
  if peticion == nil {
    http.NotFound(w, r)
    return "", errors.New("Ruta inválida")
  }
  return peticion[len(peticion) - 1], nil
}

La estructura de las peticiones que validamos nos permite saber que el título de las wiki siempre estará en la posición tamaño-1 del slice, por tanto, aprovechamos y cambiamos la posición absoluta (2) por una relativa (tamaño – 1) para no tener este problema de nuevo.

Finalmente, añadimos el manejador a la función principal para que escuche las peticiones a “/”. El código completo del servidor quedaría así:

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

type Pagina struct{
  Titulo string
  Cuerpo []byte
}

var plantillas = template.Must(template.ParseFiles("tmpl/edit.html", "tmpl/view.html", "tmpl/front.html"))
var regex_ruta = regexp.MustCompile("^(/|(/(edit|save|view)/([a-zA-Z0-9]+)))$")
var pagina_principal = "Principal"

func main() {
  http.HandleFunc("/", llamarManejador(manejadorRaiz)) //Nuevo manejador
  http.HandleFunc("/view/", llamarManejador(manejadorMostrar))
  http.HandleFunc("/save/", llamarManejador(manejadorGuardar))
  http.HandleFunc("/edit/", llamarManejador(manejadorEditar))
  
  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)
}

//Función 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
}

//Función para validar ruta y regresar nombre de la página solicitada
func dameTitulo(w http.ResponseWriter, r *http.Request) (string, error) {
  peticion := regex_ruta.FindStringSubmatch(r.URL.Path)
  if peticion == nil {
    http.NotFound(w, r)
    return "", errors.New("Ruta inválida")
  }
  return peticion[len(peticion) - 1], nil
}

//Función para cargar las plantillas HTML
func cargarPlantilla(w http.ResponseWriter, nombre_plantilla string, pagina *Pagina){
  plantillas.ExecuteTemplate(w, nombre_plantilla + ".html", pagina)
}

func llamarManejador(manejador func (http.ResponseWriter, *http.Request, string)) http.HandlerFunc {
  return func(w http.ResponseWriter, r *http.Request) {
    titulo, err := dameTitulo(w, r)
    fmt.Println(titulo)
    if err != nil {
      http.NotFound(w, r)
      return
    }
    manejador(w, r, titulo)
  }
}

//Manejador para mostrar página principal
func manejadorRaiz(w http.ResponseWriter, r *http.Request, titulo string) {
  p, err := cargarPagina(pagina_principal)
  if err != nil {
    http.Redirect(w, r, "edit/" + pagina_principal, http.StatusFound)
    fmt.Println("Error")
    return
  }
  cargarPlantilla(w, "front", p)
}

//Manejador para visualizar wikis
func manejadorMostrar(w http.ResponseWriter, r *http.Request, titulo string){
  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 string){
  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 string) {
  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 compilar y ejecutar:

go build servidor_wikis.go
./servidor_wikis

Para verificar que funciona ingresamos a:

http://localhost:8080/

Finalizando…

Espero que esta entrada te haya servido. Nos vemos en la siguiente nota. Hasta luego. See ya!

Deja una respuesta

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