Nota: Si quieres usar la autenticación con Google Protocol RPC, puedes usar la autenticación disponible para las aplicaciones de App Engine en la Google Cloud consola. También deberás especificar el requisito de inicio de sesión en el archivo app.yaml
. La biblioteca RPC de protocolos de Google de App Engine no admite otras metodologías de autenticación.
La biblioteca RPC de Protocol de Google es un framework para implementar servicios de llamada a procedimiento remoto (RPC) basados en HTTP. Un servicio RPC es un conjunto de tipos de mensajes y métodos remotos que proporcionan una forma estructurada para que las aplicaciones externas interactúen con las aplicaciones web. Como puedes definir mensajes y servicios en el lenguaje de programación Python, es fácil desarrollar servicios de Protocol RPC, probarlos y escalarlos en App Engine.
Aunque puedes usar la biblioteca RPC de Protocol de Google para cualquier tipo de servicio RPC basado en HTTP, algunos casos prácticos habituales son los siguientes:
- publicación de API web para terceros,
- creación de servidores Ajax estructurados,
- clonación a comunicación prolongada con servidores.
Puedes definir un servicio RPC de protocolo de Google en una sola clase de Python que contenga cualquier número de métodos remotos declarados. Cada método remoto acepta un conjunto de parámetros específico como solicitud y devuelve una respuesta específica. Estos parámetros de solicitud y de respuesta son clases definidas por el usuario conocidas como mensajes.
Aplicación Hello World de la llamada a procedimiento remoto (RPC) del protocolo de Google
En esta sección presentamos, a modo de ejemplo, una definición de servicio muy simple que recibe un mensaje de un cliente remoto. El mensaje contiene el nombre de un usuario (HelloRequest.my_name
) y devuelve un saludo para esa persona (HelloResponse.hello
):
from protorpc import messages from protorpc import remote from protorpc.wsgi import service package = 'hello' # Create the request string containing the user's name class HelloRequest(messages.Message): my_name = messages.StringField(1, required=True) # Create the response string class HelloResponse(messages.Message): hello = messages.StringField(1, required=True) # Create the RPC service to exchange messages class HelloService(remote.Service): @remote.method(HelloRequest, HelloResponse) def hello(self, request): return HelloResponse(hello='Hello there, %s!' % request.my_name) # Map the RPC service and path (/hello) app = service.service_mappings([('/hello.*', HelloService)])
Primeros pasos con la llamada a procedimiento remoto (RPC) del protocolo de Google
En esta sección se muestra cómo empezar a usar Google Protocol RPC con la aplicación de libro de visitas desarrollada en Python. Los usuarios pueden acceder al libro de visitas online (también incluido como demostración en el SDK Python), escribir entradas y ver las entradas de los demás. Los usuarios interactúan directamente con la interfaz, pero las aplicaciones web no pueden acceder fácilmente a esa información.
Ahí es donde entra en juego Protocol RPC. En este tutorial, aplicaremos Google Protocol RPC a este libro de visitas básico para que otras aplicaciones web puedan acceder a sus datos. En este tutorial solo se explica cómo usar Google Protocol RPC para ampliar la funcionalidad del libro de visitas. Tú decides qué hacer a continuación. Por ejemplo, es posible que quieras escribir una herramienta que lea los mensajes publicados por los usuarios y que cree un gráfico de las publicaciones diarias en orden cronológico. La forma de usar Protocol RPC depende de tu aplicación específica. Lo importante es que Protocol RPC de Google amplía considerablemente lo que puedes hacer con los datos de tu aplicación.
Para empezar, crearás un archivo, postservice.py
, que implementa métodos remotos para acceder a los datos del almacén de datos de la aplicación Guestbook.
Cómo crear el módulo PostService
El primer paso para empezar a usar Google Protocol RPC es crear un archivo llamado postservice.py
en el directorio de tu aplicación. Usarás este archivo para definir el nuevo servicio, que implementa dos métodos: uno que publica datos de forma remota y otro que los obtiene de forma remota.
No es necesario que añadas nada a este archivo ahora, pero es donde colocarás todo el código definido en las secciones posteriores. En la siguiente sección, creará un mensaje que represente una nota publicada en el almacén de datos de la aplicación del libro de visitas.
Cómo gestionar los mensajes
Los mensajes son el tipo de datos fundamental que se usa en Google Protocol RPC. Los mensajes se definen declarando una clase que hereda de la clase base Message. A continuación, especifica los atributos de clase que corresponden a cada uno de los campos del mensaje.
Por ejemplo, el servicio de libro de visitas permite a los usuarios publicar una nota. Si aún no lo has hecho, crea un archivo llamado postservice.py
en el directorio de tu aplicación. PostService usa la clase Greeting para almacenar una publicación en el almacén de datos. Vamos a definir un mensaje que represente una nota de este tipo:
from protorpc import messages class Note(messages.Message): text = messages.StringField(1, required=True) when = messages.IntegerField(2)
El mensaje de nota se define mediante dos campos: text
y when
. cada uno, de distinto tipo. El campo de texto es una cadena Unicode que representa el contenido de la publicación de un usuario en la página del libro de visitas. El campo when
es un número entero que representa la marca de tiempo de la publicación. A la hora de definir la cadena, también:
- Asigna a cada campo un valor numérico único (
1
paratext
y2
parawhen
) que el protocolo de red subyacente utiliza para identificar el campo. - Convierte
text
en un campo obligatorio. Los campos son opcionales de forma predeterminada, pero puedes marcarlos como obligatorios si definesrequired=True
. Los mensajes deben iniciarse definiendo campos obligatorios a un valor. Los métodos de servicio de la llamada a procedimiento remoto (RPC) del protocolo de Google solo aceptan mensajes inicializados correctamente.
Para establecer valores para los campos que usen el constructor de la clase Note:
# Import the standard time Python library to handle the timestamp. import time note_instance = Note(text=u'Hello guestbook!', when=int(time.time()))
Además, puedes leer y definir valores en un mensaje como si fueran valores de atributo Python normales. Por ejemplo, para cambiar el mensaje:
print note_instance.text note_instance.text = u'Good-bye guestbook!' print note_instance.text
Hello guestbook! Good-bye guestbook!
Cómo definir un servicio
Un servicio es una definición de clase que hereda de la clase base Service. Los métodos remotos de un servicio se indican mediante el decorador remote
. Cada método de servicio acepta un único mensaje como su parámetro y devuelve un único mensaje como respuesta.
Vamos a definir el primer método de PostService. Añade lo siguiente a tu archivo postservice.py
:
import datetime from protorpc import message_types from protorpc import remote import guestbook class PostService(remote.Service): # Add the remote decorator to indicate the service methods @remote.method(Note, message_types.VoidMessage) def post_note(self, request): # If the Note instance has a timestamp, use that timestamp if request.when is not None: when = datetime.datetime.utcfromtimestamp(request.when) # Else use the current time else: when = datetime.datetime.now() note = guestbook.Greeting(content=request.text, date=when, parent=guestbook.guestbook_key) note.put() return message_types.VoidMessage()
El decorador remote
usa dos parámetros:
- El tipo de solicitud esperado. El método post_note() acepta una instancia Note como tipo de solicitud.
- El tipo de solicitud esperado. La biblioteca RPC de Protocol de Google incluye un tipo integrado llamado VoidMessage (en el módulo
protorpc.message_types
), que se define como un mensaje sin campos. Esto significa que post_note() no devuelve nada útil al que realiza la invocación. Si devuelve algo sin error, el mensaje se considera publicado.
Como Note.when
es un campo opcional, es posible que el llamante no lo haya definido. Cuando esto ocurre, el valor de when
se define como None. Cuando Note.when
se define como None, post_note() crea la marca de tiempo con la hora en que recibió el mensaje.
El método remoto crea una instancia del mensaje de respuesta, que se convierte en el valor devuelto del método remoto.
Cómo registrar el servicio
Puedes publicar tu nuevo servicio como una aplicación WSGI mediante la biblioteca protorpc.wsgi.service
. Crea un archivo llamado services.py
en el directorio de tu aplicación y añade el siguiente código para crear tu servicio:
from protorpc.wsgi import service import postservice # Map the RPC service and path (/PostService) app = service.service_mappings([('/PostService', postservice.PostService)])
Ahora, añade el siguiente controlador al archivo app.yaml
, encima de la entrada catch-all:
- url: /PostService.* script: services.app - url: .* script: guestbook.app
Cómo probar el servicio desde la línea de comandos
Ahora que has creado el servicio, puedes probarlo con curl
o con una herramienta de línea de comandos similar.
# After starting the development web server: # NOTE: ProtoRPC always expect a POST. % curl -H \ 'content-type:application/json' \ -d '{"text": "Hello guestbook!"}'\ http://localhost:8080/PostService.post_note
Una respuesta JSON vacía indica que la nota se ha publicado correctamente. Puedes ver la nota dirigiéndote a la aplicación de libro de visitas en tu navegador (http://localhost:8080/).
Cómo añadir campos de mensaje
Ahora que podemos publicar mensajes en PostService, vamos a añadir un nuevo método para obtener mensajes de PostService. Primero, definiremos un mensaje de solicitud en postservice.py
que defina algunos valores predeterminados y un nuevo campo enum que indique al servidor cómo ordenar las notas en la respuesta. Defínelo encima de la clase PostService
que has definido antes:
class GetNotesRequest(messages.Message): limit = messages.IntegerField(1, default=10) on_or_before = messages.IntegerField(2) class Order(messages.Enum): WHEN = 1 TEXT = 2 order = messages.EnumField(Order, 3, default=Order.WHEN)
Cuando se envía a PostService, el mensaje solicita una determinada cantidad de notas en una fecha particular, o antes de esta, y en un orden particular. El campo limit
indica el número máximo de notas que se van a obtener. Si no se define explícitamente, limit
tiene un valor predeterminado de 10 notas (como indica el argumento de palabra clave default=10
).
El campo order introduce la clase EnumField, que habilita el tipo de campo enum
cuando el valor de un campo está restringido a un número limitado de valores simbólicos conocidos. En este caso, enum
indica al servidor cómo ordenar las notas en la respuesta. Para definir los valores del enum, crea una subclase de la clase Enum. A cada tipo se le debe asignar un nombre con su correspondiente número exclusivo. Cada número se convierte en una instancia del tipo enum, al que se puede acceder desde la clase.
print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN.name, GetNotesRequest.Order.WHEN.number)
Cada valor de enum
tiene una característica especial que facilita la conversión a su nombre o a su número. En lugar de acceder al atributo de nombre y de número, basta con convertir cada valor en una cadena o en un número entero:
print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN, GetNotesRequest.Order.WHEN)
Los campos "enum" se declaran de forma similar a otros campos, salvo que deben tener el tipo enum como primer parámetro, antes del número de campo. Estos campos también pueden tener valores predeterminados.
Cómo definir el mensaje de respuesta
Ahora vamos a definir el mensaje de respuesta de get_notes(). La respuesta debe ser un conjunto de mensajes Note. Además, los mensajes pueden contener otros mensajes. En el caso del campo Notes.notes
definido más abajo, indicamos que es una colección de mensajes proporcionando la clase Note
como primer parámetro al constructor messages.MessageField
(antes del número de campo):
class Notes(messages.Message): notes = messages.MessageField(Note, 1, repeated=True)
El campo Notes.notes
también es un campo repetido, como indica el argumento de palabra clave repeated=True
. Los valores de los campos duplicados deben ser listas del tipo de campo de la declaración. En este caso, Notes.notes
debe ser una lista de instancias de Note. Las listas se crean automáticamente y no se les puede asignar el valor None.
A continuación, presentamos un ejemplo de cómo crear un objeto Notes:
response = Notes(notes=[Note(text='This is note 1'), Note(text='This is note 2')]) print 'The first note is:', response.notes[0].text print 'The second note is:', response.notes[1].text
Cómo implementar get_notes
Ahora, podemos añadir el método get_notes() a la clase PostService:
import datetime import time from protorpc import remote class PostService(remote.Service): @remote.method(GetNotesRequest, Notes) def get_notes(self, request): query = guestbook.Greeting.query().order(-guestbook.Greeting.date) if request.on_or_before: when = datetime.datetime.utcfromtimestamp( request.on_or_before) query = query.filter(guestbook.Greeting.date <= when) notes = [] for note_model in query.fetch(request.limit): if note_model.date: when = int(time.mktime(note_model.date.utctimetuple())) else: when = None note = Note(text=note_model.content, when=when) notes.append(note) if request.order == GetNotesRequest.Order.TEXT: notes.sort(key=lambda note: note.text) return Notes(notes=notes)