Hasta ahora, cada vez que el cliente pedía una página a nuestro servidor, la plantilla correspondiente necesitaba ser cargada desde el disco. Esto puede llegar a ser contraproducente si, por ejemplo, muchos clientes realizaran peticiones al mismo tiempo, se necesitarían cargar las mismas plantillas una y otra vez, ocasionando retrasos en los tiempos de respuesta. En esta entrada aprenderemos cómo almacenar las plantillas en memoria para cargarlas solamente una vez, al inicio del programa.
Comenzamos
Haremos uso del, previamente incluido, paquete html/template para cargar las plantillas al principio.
Crearemos una variable tipo *Template (apuntador a Template), tipo de dato incluido con el paquete html/template que nos servirá para almacenar las plantillas. Haremos uso del método template.ParseFiles() de la siguiente forma:
var plantillas, err = template.ParseFiles( "edit.html", "view.html" )
La variable err es necesaria porque el método template.ParseFiles() devuelve dos valores: el apuntador y una variable tipo error; sin almacenar esa segunda variable, nuestro programa no compilaría. Es buena práctica validar los errores de nuestros programas, en entradas posteriores te enseñaré a hacerlo pero por ahora no será necesario.
Cuando intentamos acceder a una plantilla que no existe, ocurre un error fatal y se despliega una serie de códigos de error en consola, pero esto sucede en ejecución y no hay forma de prevenirlo con el código que agregamos anteriormente. Si quieres que tu programa se detenga al inicio de su ejecución cuando no puede cargar una plantilla en lugar de esperar hasta que se haga una petición, puedes utilizar el método template.Must() también incluido en el paquete html/template:
var plantillas = template.Must(template.ParseFiles( "edit.html" , "view.html" ))
Este método envuelve los dos datos que nos regresa template.ParseFiles(). Al momento que detecta que algo falló al cargar las plantillas, pone al programa en estado de «pánico» (panic) e inmediatamente termina su ejecución. Dejamos de utilizar la variable err porque el método template.Must() es el encargado de procesar los errores regresados por template.ParseFiles() y “filtrar” solamente el *Template.
Finalmente, haremos unos cambios a la función cargarPlantilla() para que utilice la variable plantillas. Quedaría de la siguiente manera:
func cargarPlantilla( w http.ResponseWritter, nombre_plantilla string, pagina *Pagina) { plantillas.ExecuteTemplate(w, nombre_plantilla + ".html", pagina) }
Programa completo
El siguiente es el código fuente del servidor con todas las modificaciones que hemos hecho hasta ahora:
package main import ( "fmt" "io/ioutil" "net/http" "html/template" ) type Pagina struct{ Titulo string Cuerpo []byte } var plantillas = template.Must(template.ParseFiles("edit.html", "view.html")) 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, nombre_plantilla string, pagina *Pagina){ plantillas.ExecuteTemplate(w, nombre_plantilla + ".html", pagina) } //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) }
Después de compilar tu servidor y ponerlo en ejecución, recuerda acceder a la siguiente dirección desde tu navegador para realizar pruebas:
localhost:8080/view/Ejemplo
Finalizando…
Espero que te haya servido lo visto en esta entrada. Cualquier duda puedes dejarla en la sección de comentarios y a la brevedad te ayudaré a resolverla.
En la siguiente entrada aprenderemos a utilizar regex (expresiones regulares) en nuestro servidor para validar las URL. Hasta la próxima. See ya!
Buenos días, muy buen ejemplo, tengo una duda, cuando quieras usar archivos css cómo se le hace?
Hola, gracias por leernos.
Necesitas agregar el enlace al archivo externo o incluir el estilo directamente en el código fuente de las paginas como lo harías con cualquier archivo .html.
Gracias por compartir tus conocimientos. Me ha sido de gran ayuda.
Gracias a ti por leernos.
muy bueno, felicitaciones
Grascias a ti por leerno.