Manejar sesiones con Firestore


En este instructivo, se muestra cómo manejar sesiones en App Engine.

En muchas apps, se necesita controlar las sesiones para la autenticación y las preferencias del usuario. El paquete sessions del kit de herramientas web Gorilla incluye una implementación basada en un sistema de archivos para realizar esta función. Sin embargo, esta implementación no es adecuada para las apps que se pueden entregar desde varias instancias, ya que la sesión que se registra en una instancia puede diferir de las que se registran en otras. Además, el paquete gorilla/sessions incluye una implementación basada en cookies. Sin embargo, esta implementación requiere que se encripten cookies y se almacene toda la sesión en el cliente, en lugar de solo un ID de sesión, que puede ser muy largo para algunas apps.

Objetivos

  • Escribe la aplicación.
  • Ejecuta la aplicación de manera local.
  • Implementa la aplicación en App Engine.

Costos

En este documento, usarás los siguientes componentes facturables de Google Cloud:

Para generar una estimación de costos en función del uso previsto, usa la calculadora de precios. Es posible que los usuarios nuevos de Google Cloud califiquen para obtener una prueba gratuita.

Cuando finalices las tareas que se describen en este documento, puedes borrar los recursos que creaste para evitar que continúe la facturación. Para obtener más información, consulta Cómo realizar una limpieza.

Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  4. Habilita la API de Firestore.

    Habilita la API

  5. Instala Google Cloud CLI.
  6. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  7. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  8. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  9. Habilita la API de Firestore.

    Habilita la API

  10. Instala Google Cloud CLI.
  11. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  12. Actualiza los componentes de gcloud:
    gcloud components update
  13. Prepara tu entorno de desarrollo.

Configura el proyecto

  1. En la ventana de la terminal, clona el repositorio de la app de muestra en la máquina local:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
  2. Ve al directorio que contiene el código de muestra:

    cd golang-samples/getting-started/sessions

Comprende cómo funciona la app web

Esta app muestra saludos en diferentes idiomas para cada usuario. A los usuarios recurrentes se los saluda siempre en el mismo idioma.

Varias ventanas de la app en las que se muestra un saludo en diferentes idiomas

Para que la app pueda guardar las preferencias de un usuario, debes almacenar la información sobre el usuario actual en una sesión. Esta app de muestra usa Firestore para almacenar los datos de las sesiones.

Puedes usar firestoregorilla, un almacén de sesiones compatible con gorilla/sessions.

  1. Primero, la app importa las dependencias y define el tipo de app para conservar un sessions.Store y una plantilla HTML, y determina la lista de saludos.

    
    // Command sessions starts an HTTP server that uses session state.
    package main
    
    import (
    	"context"
    	"fmt"
    	"html/template"
    	"log"
    	"math/rand"
    	"net/http"
    	"os"
    
    	"cloud.google.com/go/firestore"
    	firestoregorilla "github.com/GoogleCloudPlatform/firestore-gorilla-sessions"
    	"github.com/gorilla/sessions"
    )
    
    // app stores a sessions.Store. Create a new app with newApp.
    type app struct {
    	store sessions.Store
    	tmpl  *template.Template
    }
    
    // greetings are the random greetings that will be assigned to sessions.
    var greetings = []string{
    	"Hello World",
    	"Hallo Welt",
    	"Ciao Mondo",
    	"Salut le Monde",
    	"Hola Mundo",
    }
    
  2. A continuación, la app define una función main, que crea una nueva instancia de app, registra el controlador de índices y, luego, inicia el servidor HTTP. La función newApp crea la instancia de app mediante la inicialización de un sessions.Store con la función firestoregorilla.New.

    
    func main() {
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    	projectID := os.Getenv("GOOGLE_CLOUD_PROJECT")
    	if projectID == "" {
    		log.Fatal("GOOGLE_CLOUD_PROJECT must be set")
    	}
    
    	a, err := newApp(projectID)
    	if err != nil {
    		log.Fatalf("newApp: %v", err)
    	}
    
    	http.HandleFunc("/", a.index)
    
    	log.Printf("Listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    // newApp creates a new app.
    func newApp(projectID string) (*app, error) {
    	ctx := context.Background()
    	client, err := firestore.NewClient(ctx, projectID)
    	if err != nil {
    		log.Fatalf("firestore.NewClient: %v", err)
    	}
    	store, err := firestoregorilla.New(ctx, client)
    	if err != nil {
    		log.Fatalf("firestoregorilla.New: %v", err)
    	}
    
    	tmpl, err := template.New("Index").Parse(`<body>{{.views}} {{if eq .views 1.0}}view{{else}}views{{end}} for "{{.greeting}}"</body>`)
    	if err != nil {
    		return nil, fmt.Errorf("template.New: %w", err)
    	}
    
    	return &app{
    		store: store,
    		tmpl:  tmpl,
    	}, nil
    }
    
  3. El controlador de índices obtiene la sesión del usuario y, si es necesario, crea una. A las sesiones nuevas se les asigna un idioma aleatorio y un recuento de vistas de 0. Luego el recuento de vistas aumenta a uno, la sesión se guarda, y la plantilla HTML escribe la respuesta.

    
    // index uses sessions to assign users a random greeting and keep track of
    // views.
    func (a *app) index(w http.ResponseWriter, r *http.Request) {
    	if r.RequestURI != "/" {
    		return
    	}
    
    	// name is a non-empty identifier for this app's sessions. Set it to
    	// something descriptive for your app. It is used as the Firestore
    	// collection name that stores the sessions.
    	name := "hello-views"
    	session, err := a.store.Get(r, name)
    	if err != nil {
    		// Could not get the session. Log an error and continue, saving a new
    		// session.
    		log.Printf("store.Get: %v", err)
    	}
    
    	if session.IsNew {
    		// firestoregorilla uses JSON, which unmarshals numbers as float64s.
    		session.Values["views"] = float64(0)
    		session.Values["greeting"] = greetings[rand.Intn(len(greetings))]
    	}
    	session.Values["views"] = session.Values["views"].(float64) + 1
    	if err := session.Save(r, w); err != nil {
    		log.Printf("Save: %v", err)
    		// Don't return early so the user still gets a response.
    	}
    
    	if err := a.tmpl.Execute(w, session.Values); err != nil {
    		log.Printf("Execute: %v", err)
    	}
    }
    

    En el siguiente diagrama, se muestra cómo Firestore controla las sesiones de la app de App Engine.

    Diagrama de arquitectura: usuario, App Engine, Firestore

Borra sesiones

firestoregorilla no borra las sesiones antiguas o vencidas. Puedes borrar los datos de las sesiones en Google Cloud Console o implementar una estrategia de eliminación automática. Cuando usas soluciones de almacenamiento para sesiones, como Memcache o Redis, las sesiones vencidas se borran de forma automática.

Ejecución local

  1. En la ventana de la terminal, compila el objeto binario sessions:

    go build
    
  2. Inicia el servidor HTTP:

    ./sessions
    
  3. Visualiza la app en el navegador web:

    Cloud Shell

    En la barra de herramientas de Cloud Shell, haz clic en Vista previa en la Web Vista previa en la Web y selecciona Vista previa en el puerto 8080.

    Máquina local

    En tu navegador, ve a http://localhost:8080.

    Ves uno de los cinco saludos: "Hello World", "Hallo Welt", "Hola mundo", "Salut le Monde" o "Ciao Mondo". El idioma cambia si abres la página en otro navegador o en modo Incógnito. Puedes ver y editar los datos de la sesión en la consola de Google Cloud.

    Sesiones de Firestore en la consola de Google Cloud

  4. Para detener el servidor HTTP, presiona Control+C en la ventana de la terminal.

Implementa y ejecuta en App Engine

Puedes usar el entorno estándar de App Engine para compilar y también implementar una aplicación que se ejecute de manera confiable con cargas pesadas y grandes cantidades de datos.

En este instructivo, se usa el entorno estándar de App Engine para implementar el servidor.

El archivo app.yaml contiene la configuración del entorno estándar de App Engine:

runtime: go112
  1. Implementa la app en App Engine con el siguiente comando:

    gcloud app deploy
    
  2. En el navegador, ingresa la siguiente URL:

    https://PROJECT_ID.REGION_ID.r.appspot.com

    Reemplaza lo siguiente:

El saludo ahora se entrega en un servidor web que se ejecuta en una instancia de App Engine.

Depura la app

Si no puedes conectarte a la aplicación de App Engine, verifica lo siguiente:

  1. Comprueba que los comandos de implementación de gcloud se completaron correctamente y no generaron ningún error. Si hubo errores (por ejemplo, message=Build failed), corrígelos y, luego, intenta implementar la aplicación de App Engine nuevamente.
  2. En la consola de Google Cloud, ve a la página Explorador de registros.

    Ir a la página Explorador de registros

    1. En la lista desplegable Recursos seleccionados recientemente, haz clic en Aplicación de App Engine y, luego, haz clic en Todos module_id. Verás una lista de las solicitudes de cuando visitaste tu aplicación. Si no ves una lista de solicitudes, confirma que seleccionaste Todos module_id en la lista desplegable. Si ves mensajes de error impresos en la consola de Google Cloud, verifica que el código de tu app coincida con el código de la sección sobre escritura de la app web.

    2. Asegúrate de que la API de Firestore esté habilitada.

Limpia

Borra el proyecto

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra la instancia de App Engine

  1. En la consola de Google Cloud, ve a la página Versiones de App Engine.

    Ir a Versiones

  2. Selecciona la casilla de verificación de la versión no predeterminada de la app que deseas borrar.
  3. Para borrar la versión de la app, haz clic en Borrar.

¿Qué sigue?