Cómo implementar la función multiusuario con espacios de nombres

La API de Namespaces te permite habilitar con facilidad la función multiusuario en tu aplicación; para ello, solo selecciona una string de espacio de nombres para cada instancia con la función appengine.Namespace.

Cómo configurar el espacio de nombres actual

Puedes configurar espacios de nombres con appengine.Namespace. Esto muestra un contexto nuevo en el que las API habilitadas con espacios de nombres utilizarán el espacio de nombres específico automáticamente.

La mayoría de los desarrolladores de App Engine usarán su dominio de G Suite como el espacio de nombres actual. G Suite te permite implementar tu aplicación en cualquiera de tus dominios, por lo que puedes usar fácilmente este mecanismo para configurar espacios de nombres diferentes para dominios diferentes. Luego, puedes usar esos espacios de nombres independientes para segregar datos en todos los dominios. Si deseas obtener más información sobre la configuración de varios dominios en G Suite Dashboard, consulta Cómo implementar tu aplicación en tu URL de G Suite.

Si no especificas un valor para namespace, el espacio de nombres se establece en una string vacía. La string namespace es arbitraria, pero también se limita a un máximo de 100 caracteres alfanuméricos, puntos, guiones bajos y guiones. Más explícitamente, las strings de espacio de nombres deben coincidir con la expresión regular [0-9A-Za-z._-]{0,100}.

Por regla general, todos los espacios de nombres que comienzan con "_" (guion bajo) se reservan para el uso del sistema. La regla de espacio de nombres del sistema no es obligatoria, pero puedes encontrar fácilmente consecuencias negativas no definidas si no la respetas.

Cómo evitar la filtración de datos

Uno de los riesgos asociados comúnmente con las aplicaciones multiusuario es el peligro de que los datos se filtren a través de los espacios de nombres. Las filtraciones de datos no deseadas pueden surgir de varias fuentes, incluidas las siguientes:

  • El uso de espacios de nombres con las API de App Engine que todavía no admiten los espacios de nombres. Por ejemplo, Blobstore no admite espacios de nombres. Si usas Espacios de nombres con Blobstore, debes evitar el uso de consultas de Blobstore para solicitudes de usuario final, o claves de Blobstore de fuentes no confiables.
  • El uso de un medio de almacenamiento externo (en lugar de Memcache y almacén de datos), a través de URL Fetch o de algún otro mecanismo, sin proporcionar un esquema de compartimentación para los espacios de nombres.
  • La configuración de un espacio de nombres en función del dominio de correo electrónico del usuario. En la mayoría de los casos, no querrás que todas las direcciones de correo electrónico de un dominio tengas acceso a un espacio de nombres. El uso del dominio de correo electrónico también evita que tu aplicación use un espacio de nombres hasta que el usuario haya accedido.

Cómo implementar espacios de nombres

En las secciones siguientes se describe cómo implementar espacios de nombres con otras API y herramientas de App Engine.

Cómo crear espacios de nombres por usuario

Algunas aplicaciones necesitan crear espacios de nombres por usuario. Si deseas compartimentar datos a nivel del usuario para los usuarios que hayan accedido a la aplicación, considera usar user.Current(c).ID, que muestra un ID único y permanente para el usuario. El siguiente ejemplo de código muestra cómo usar las API de usuarios con este propósito:

import (
	"golang.org/x/net/context"

	"google.golang.org/appengine"
	"google.golang.org/appengine/user"
)

func namespace(ctx context.Context) context.Context {
	// assumes the user is logged in.
	ctx, err := appengine.Namespace(ctx, user.Current(ctx).ID)
	if err != nil {
		// ...
	}
	return ctx
}

Normalmente, las aplicaciones que crean espacios de nombres por usuario también proporcionan páginas de destino específicas a usuarios diferentes. En estos casos, la aplicación necesita proporcionar un esquema de URL que dicte qué página de destino se le debe mostrar al usuario.

Cómo usar espacios de nombres con Datastore

De forma predeterminada, el almacén de datos usa el espacio de nombres actual para solicitudes del almacén de datos. La API aplica este espacio de nombres actual a los objetos datastore.Key cuando se crean. Por lo tanto, debes tener cuidado si una aplicación almacena objetos Key en formularios serializados, ya que el espacio de nombres se conserva en esas serializaciones.

Si usas objetos Key deserializados, asegúrate de que se comporten como se espera. La mayoría de las aplicaciones simples que usan un almacén de datos (put/query/get) sin usar otros mecanismos de almacenamiento, funcionarán como se espera mediante la configuración del espacio de nombres actual antes de llamar a cualquier API del almacén de datos.

El ejemplo de código siguiente muestra el controlador de solicitudes SomeRequest para incrementar el recuento del espacio de nombres actual y el espacio de nombres llamado arbitrariamente -global- en una entidad de almacén de datos Counter.
import (
	"io"
	"net/http"

	"golang.org/x/net/context"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
)

type Counter struct {
	Count int64
}

func incrementCounter(ctx context.Context, name string) error {
	key := datastore.NewKey(ctx, "Counter", name, 0, nil)
	return datastore.RunInTransaction(ctx, func(ctx context.Context) error {
		var ctr Counter
		err := datastore.Get(ctx, key, &ctr)
		if err != nil && err != datastore.ErrNoSuchEntity {
			return err
		}
		ctr.Count++
		_, err = datastore.Put(ctx, key, &ctr)
		return err
	}, nil)
}

func someHandler(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	err := incrementCounter(ctx, "SomeRequest")
	if err != nil {
		// ... handle err
	}

	// temporarily use a new namespace
	{
		ctx, err := appengine.Namespace(ctx, "-global-")
		if err != nil {
			// ... handle err
		}
		err = incrementCounter(ctx, "SomeRequest")
		if err != nil {
			// ... handle err
		}
	}

	io.WriteString(w, "Updated counters.\n")
}

func init() {
	http.HandleFunc("/some_url", someHandler)
}

Usa espacios de nombres con Memcache

Memcache usa el espacio de nombres de appengine.Context que se utiliza para las llamadas a la API de Memcache.

Cómo usar espacios de nombres con la lista de tareas en cola

Las listas de aplicaciones en cola utilizan el espacio de nombres como se establece en appengine.Context en el momento que se agrega la tarea.

Los nombres de las tareas se comparten en todos los espacios de nombres. No puedes crear dos tareas con el mismo nombre, aunque usen espacios de nombres diferentes. Si deseas usar el mismo nombre de tarea para varios espacios de nombres, puedes simplemente agregar los espacios de nombres al nombre de la tarea.

Existen algunas instancias únicas en las que es apropiado configurar un espacio de nombres de manera explícita para una tarea que funcione en todos los espacios de nombres. Por ejemplo, puedes crear una tarea que acumule estadísticas de uso a través de todos los espacios de nombres. Podrás entonces configurar explícitamente el espacio de nombres de la tarea. El siguiente ejemplo de código muestra cómo configurar de manera explícita los espacios de nombres con la lista de tareas en cola.

import (
	"io"
	"net/http"

	"golang.org/x/net/context"

	"google.golang.org/appengine"
	"google.golang.org/appengine/datastore"
	"google.golang.org/appengine/taskqueue"
)

type Counter struct {
	Count int64
}

func incrementCounter(ctx context.Context, name string) error {
	key := datastore.NewKey(ctx, "Counter", name, 0, nil)
	return datastore.RunInTransaction(ctx, func(ctx context.Context) error {
		var ctr Counter
		err := datastore.Get(ctx, key, &ctr)
		if err != nil && err != datastore.ErrNoSuchEntity {
			return err
		}
		ctr.Count++
		_, err = datastore.Put(ctx, key, &ctr)
		return err
	}, nil)
}

// taskQueueHandler serves /_ah/counter.
func taskQueueHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "POST only", http.StatusMethodNotAllowed)
		return
	}
	ctx := appengine.NewContext(r)
	err := incrementCounter(ctx, r.FormValue("counter_name"))
	if err != nil {
		// ... handle err
	}
}

func someRequest(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)

	// Perform asynchronous requests to update counter.
	// (missing error handling here.)
	t := taskqueue.NewPOSTTask("/_ah/counter", map[string][]string{
		"counter_name": {"someRequest"},
	})

	taskqueue.Add(ctx, t, "")

	// temporarily use a new namespace
	{
		ctx, err := appengine.Namespace(ctx, "-global-")
		if err != nil {
			// ... handle err
		}
		taskqueue.Add(ctx, t, "")
	}

	io.WriteString(w, "Counters will be updated.\n")
}

func init() {
	http.HandleFunc("/_ah/counter", taskQueueHandler)
	http.HandleFunc("/some_request", someRequest)
}

Cómo usar espacios de nombres con Blobstore

Blobstore no está segmentado por espacio de nombres. Para conservar un espacio de nombres en Blobstore, debes acceder a Blobstore a través de un medio de almacenamiento que tenga en cuenta el espacio de nombres (actualmente solo Memcache, almacén de datos y lista de tareas en cola). Por ejemplo, si una Key de BLOB se almacena en una entidad de almacén de datos, puedes acceder con una Key de almacén de datos que tiene en cuenta el espacio de nombres.

Si una aplicación accede a Blobstore por medio de claves guardadas en un almacenamiento que tiene en cuenta el espacio de nombres, Blobstore no necesita segmentarse por espacio de nombres. Las aplicaciones deben evitar las filtraciones de BLOB entre espacios de nombres mediante las siguientes acciones:

  • No usar BlobInfo para las solicitudes de usuario final. Puedes usar las consultas de BlobInfo para solicitudes administrativas (como la creación de informes sobre todos los BLOB de aplicaciones), pero usarlas para solicitudes de usuario final puede provocar filtraciones de datos, ya que no todos los registros de BlobInfo están compartimentados por espacio de nombres.
  • No deben usar claves de Blobstore de fuentes no confiables.

Cómo configurar espacios de nombres para consultas de Datastore

En Google Cloud Platform Console, puedes configurar el espacio de nombres para consultas de Datastore.

Si no quieres usar el predeterminado, selecciona el espacio de nombres que deseas usar del menú desplegable.

Cómo usar espacios de nombres con el cargador masivo

El cargador masivo admite una marca --namespace=NAMESPACE que te permite especificar el espacio de nombres que usar. Cada espacio de nombres se maneja de manera independiente y, si deseas acceder a todos los espacios de nombres, deberás iterar a través de ellos.

Las operaciones de búsqueda utilizan espacios de nombres asociados con un contexto que se les transfiere cuando se los llama.

Para realizar una operación en un espacio de nombres, debes unir un contexto con appengine.Namespace, y utilizar ese contexto cuando llames a los métodos en un índice. En el ejemplo que se muestra a continuación, el contexto se une en el espacio de nombres "aSpace":

// Open an index
index, err := search.Open("index")

// Wrap the context into a namespace
c = appengine.Namespace(c, "aSpace")

// The Search operation occurs in the namespace "aSpace"
it := index.Search(c, "hello", nil)
¿Te ha resultado útil esta página? Enviar comentarios:

Enviar comentarios sobre...

Entorno estándar de App Engine para Go