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!

9 comments

  1. hola , hasta ahora todo me ha funcionado muy bien pero intenté añadirle contenido a la página view.html o usar otra diferente q yo estoy creando y el navegador me lo carga en blanco,,
    Me pudieras decir xq??

    1. Hola, gracias por leernos. Depende de qué agregaste y cómo accedes a él. ¿Podrías publicar tu código y cómo intentas acceder a la página? Gracias.

      1. Mira este es el codigo :
        var uploadTemplate,_ = template.ParseFiles(«Index.html»)

        func main() {

        rtr :=mux.NewRouter()
        rtr.HandleFunc(«/index»,ManejadorIndex)
        rtr.PathPrefix(«/CSS/»).Handler(http.StripPrefix(«/CSS/»,
        http.FileServer(http.Dir(«./CSS»))))
        rtr.PathPrefix(«/JS/»).Handler(http.StripPrefix(«/JS/»,
        http.FileServer(http.Dir(«./JS»))))
        fmt.Println(«El servidor se ha iniciado»)

        http.Handle(«/»,rtr)
        http.ListenAndServe(«:9090», nil)
        }
        func Upload(w http.ResponseWriter, r *http.Request) {
        uploadTemplate.Execute(w, nil)
        fmt.Println(uploadTemplate)
        }

        func ManejadorIndex(w http.ResponseWriter, r *http.Request) {
        plantilla,err:=template.ParseFiles(«Index.html»)
        if err!=nil {
        fmt.Println(«Error:»,err)
        return
        }
        plantilla.Execute(w,nil)
        fmt.Println(plantilla)

        }
        Index. html es una pagina simple que tiene implementado un mapa usando leaflet y unas tablas con fechas de hechos que estan ubicados en el mapa, la pagina entera en el navegador me abre bien, cuando la cargo por el servidor de go con el plantilla.Execute no me abre si le pongo mucho contenido.Me explico: yo probe a ver asta cuanto me cargaba y se me quedo en el mapa, una tabla y parte de la otra, si le agrego más el navegador lo carga en blanco. Estoy usando Goland , y este tambien ejecuta un servidor web para los html y si me carga la página completa.Por lo que quisiera saber como puedo levantar ese html completo para a partir de ahí añadirle contenido dinámico porque no resuelvo nada buscado como agregarle funcionalidades a la página a medias.

  2. hola, soy nuevo en GO y me gustaría una ayuda con esto: como puedo desarrollar un programa para crear paginas web donde los datos se obtengan desde un archivo de texto. Es como un constructor de webs así como wix.

    1. Hola, gracias por leernos. Si te refieres a un programa que lea un archivo de texto y cree páginas web en base a él, esta temporada habla de ello, te sugiero que le eches un ojo a las demás entradas, te podrían solucionar tus dudas.

  3. Me gustaría saber si es posible compilar el server web y que los archivos .html , .CSS, .js , etc no queden a la vista del usuario donde se ejecutará, sino que queden dentro del empaquetado como los archivos .go .

    Saludos y muy buena la pagina

    1. Hola, no comprendo muy bien tu pregunta. ¿Quieres que no esté a la vista de los usuarios la dirección o los archivos? Los archivos estarán a la vista del usuario ya que ellos deben de acceder a ellos para mirarlos. ¿O a que te refieres? ¿Podrías poner un ejemplo?

      Gracias por leernos.

      1. una pregunta esta esta variable::: var plantillas = template.Must(template.ParseFiles(«tmpl/edit.html», «tmpl/view.html», «tmpl/front.html»))

        y le pasamos cada plantilla html..?hay forma de decirle que busque todas?

Deja una respuesta

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