Nota: se quiser usar a autenticação com o RPC do Protocolo Google, pode usar a autenticação atualmente disponível para apps do App Engine na Google Cloud consola. Também tem de especificar o requisito de início de sessão de utilização no seu ficheiro
app.yaml
. Atualmente, a biblioteca Google Protocol RPC no App Engine não suporta outras metodologias de autenticação.
A biblioteca RPC do Protocol Buffer da Google é uma framework para implementar serviços de chamadas de procedimentos remotos (RPC) baseados em HTTP. Um serviço RPC é uma coleção de tipos de mensagens e métodos remotos que oferecem uma forma estruturada para as aplicações externas interagirem com as aplicações Web. Uma vez que pode definir mensagens e serviços na linguagem de programação Python, é fácil desenvolver serviços RPC de protocolo, testar esses serviços e escalá-los no App Engine.
Embora possa usar a biblioteca de RPC do Protocolo Google para qualquer tipo de serviço de RPC baseado em HTTP, alguns exemplos de utilização comuns incluem:
- Publicar APIs Web para utilização por terceiros
- Criar back-ends Ajax estruturados
- Clonagem para comunicação com o servidor de execução prolongada
Pode definir um serviço RPC do protocolo Google numa única classe Python que contenha qualquer número de métodos remotos declarados. Cada método remoto aceita um conjunto específico de parâmetros como um pedido e devolve uma resposta específica. Estes parâmetros de pedido e resposta são classes definidas pelo utilizador conhecidas como mensagens.
O Hello World da RPC do Protocolo Google
Esta secção apresenta um exemplo de uma definição de serviço muito simples que recebe uma mensagem de um cliente remoto. A mensagem contém o nome de um utilizador (HelloRequest.my_name
) e envia uma saudação a essa pessoa (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)])
Introdução à RPC do Protocol Buffer da Google
Esta secção demonstra como começar a usar o RPC de protocolo Google com a aplicação guestbook desenvolvida em Python. Os utilizadores podem visitar o livro de visitas (também incluído como uma demonstração no SDK Python) online, escrever entradas e ver entradas de todos os utilizadores. Os utilizadores interagem diretamente com a interface, mas não existe uma forma de as aplicações Web acederem facilmente a essas informações.
É aqui que entra o Protocol RPC. Neste tutorial, vamos aplicar o RPC do Protocolo Google a este livro de visitas básico, permitindo que outras aplicações Web acedam aos dados do livro de visitas. Este tutorial apenas aborda a utilização do Google Protocol RPC para expandir a funcionalidade do livro de visitas. Cabe-lhe a si decidir o que fazer a seguir. Por exemplo, pode querer escrever uma ferramenta que leia as mensagens publicadas pelos utilizadores e crie um gráfico de séries cronológicas de publicações por dia. A forma como usa o Protocol RPC depende da sua app específica. O importante é que o Protocol RPC da Google expande significativamente o que pode fazer com os dados da sua aplicação.
Para começar, vai criar um ficheiro, postservice.py
, que implementa métodos remotos para aceder aos dados no arquivo de dados da aplicação de livro de visitas.
Criar o módulo PostService
O primeiro passo para começar a usar o Google Protocol RPC é criar um ficheiro denominado postservice.py
no diretório da aplicação. Vai usar este ficheiro para definir o novo serviço, que implementa dois métodos: um que publica dados remotamente e outro que obtém dados remotamente.
Não tem de adicionar nada a este ficheiro agora, mas é neste ficheiro que vai colocar todo o código definido nas secções subsequentes. Na secção seguinte, vai criar uma mensagem que representa uma nota publicada no arquivo de dados da aplicação de livro de visitas.
Trabalhar com a app Mensagens
As mensagens são o tipo de dados fundamental usado no Google Protocol RPC. As mensagens são definidas declarando uma classe que herda da classe base Message. Em seguida, especifica atributos de classe que correspondem a cada um dos campos da mensagem.
Por exemplo, o serviço de livro de visitas permite que os utilizadores publiquem uma nota. Se ainda não o fez, crie um ficheiro denominado postservice.py
no diretório da aplicação. O PostService usa a classe Greeting para armazenar uma publicação no arquivo de dados. Vamos definir uma mensagem que represente essa nota:
from protorpc import messages class Note(messages.Message): text = messages.StringField(1, required=True) when = messages.IntegerField(2)
A mensagem da nota é definida por dois campos: text
e when
. Cada campo tem um tipo específico. O campo de texto é uma string Unicode que representa o conteúdo da publicação de um utilizador na página do livro de visitas. O campo when
é um número inteiro que representa a data/hora da publicação. Ao definir a string, também:
- Atribua a cada campo um valor numérico único (
1
paratext
e2
parawhen
) que o protocolo de rede subjacente usa para identificar o campo. - Tornar
text
um campo obrigatório. Os campos são opcionais por predefinição. Pode marcá-los como obrigatórios definindorequired=True
. As mensagens têm de ser inicializadas definindo os campos obrigatórios para um valor. Os métodos de serviço RPC do Protocol Buffer da Google só aceitam mensagens devidamente inicializadas.
Pode definir valores para os campos através do construtor da classe Note:
# Import the standard time Python library to handle the timestamp. import time note_instance = Note(text=u'Hello guestbook!', when=int(time.time()))
Também pode ler e definir valores numa mensagem como valores de atributos Python normais. Por exemplo, para alterar a mensagem:
print note_instance.text note_instance.text = u'Good-bye guestbook!' print note_instance.text
Hello guestbook! Good-bye guestbook!
Definir um serviço
Um serviço é uma definição de classe que herda da classe base Service. Os métodos remotos de um serviço são indicados através do decorador remote
. Cada método de um serviço aceita uma única mensagem como parâmetro e devolve uma única mensagem como resposta.
Vamos definir o primeiro método do PostService. Adicione o seguinte ao ficheiro 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()
O decorador remote
recebe dois parâmetros:
- O tipo de pedido esperado. O método post_note() aceita uma instância de Note como o respetivo tipo de pedido.
- O tipo de resposta esperado. A biblioteca RPC do protocolo Google inclui um tipo incorporado denominado VoidMessage (no módulo
protorpc.message_types
), que é definido como uma mensagem sem campos. Isto significa que a mensagem post_note() não devolve nada útil ao respetivo autor da chamada. Se for devolvida sem erro, considera-se que a mensagem foi publicada.
Uma vez que Note.when
é um campo opcional, pode não ter sido definido pelo autor da chamada. Quando isto acontece, o valor de when
é definido como Nenhum. Quando Note.when
está definido como Nenhum, post_note() cria a data/hora com base na hora em que recebeu a mensagem.
A mensagem de resposta é instanciada pelo método remoto e torna-se o valor de retorno do método remoto.
Registar o serviço
Pode publicar o seu novo serviço como uma aplicação WSGI através da biblioteca protorpc.wsgi.service
. Crie um novo ficheiro denominado services.py
no diretório da aplicação e adicione o seguinte código para criar o seu serviço:
from protorpc.wsgi import service import postservice # Map the RPC service and path (/PostService) app = service.service_mappings([('/PostService', postservice.PostService)])
Agora, adicione o seguinte controlador ao seu ficheiro app.yaml
acima da entrada geral existente:
- url: /PostService.* script: services.app - url: .* script: guestbook.app
Testar o serviço a partir da linha de comandos
Agora que criou o serviço, pode testá-lo através de curl
ou de uma ferramenta de linha de comandos semelhante.
# 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
Uma resposta JSON vazia indica que a nota foi publicada com êxito. Pode ver a nota acedendo à aplicação de livro de visitas no navegador (http://localhost:8080/).
Adicionar campos de mensagens
Agora que podemos publicar mensagens no PostService, vamos adicionar um novo método para receber mensagens do PostService. Primeiro, vamos definir uma mensagem de pedido em postservice.py
que define alguns valores predefinidos e um novo campo de enumeração que indica ao servidor como ordenar as notas na resposta. Defina-o acima da classe PostService
que definiu anteriormente:
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)
Quando enviada para o PostService, esta mensagem pede um número de notas numa determinada data ou antes desta e numa ordem específica. O campo limit
indica o número máximo de notas a obter. Se não for definido explicitamente, limit
é predefinido para 10 notas (conforme indicado pelo argumento da palavra-chave default=10
).
O campo de ordem introduz a classe EnumField, que ativa o tipo de campo enum
quando o valor de um campo está restrito a um número limitado de valores simbólicos conhecidos. Neste caso, o enum
indica ao servidor como ordenar as notas na resposta. Para definir os valores enum, crie uma subclasse da classe Enum. A cada nome tem de ser atribuído um número exclusivo para o tipo. Cada número é convertido numa instância do tipo de enumeração e pode ser acedido a partir da classe.
print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN.name, GetNotesRequest.Order.WHEN.number)
Cada valor enum
tem uma característica especial que facilita a conversão para o respetivo nome ou número. Em vez de aceder ao atributo de nome e número, basta converter cada valor numa string ou num número inteiro:
print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN, GetNotesRequest.Order.WHEN)
Os campos de enumeração são declarados de forma semelhante a outros campos, exceto que têm de ter o tipo de enumeração como o primeiro parâmetro antes do número do campo. Os campos enum também podem ter valores predefinidos.
Definir a mensagem de resposta
Agora, vamos definir a mensagem de resposta get_notes(). A resposta tem de ser uma coleção de mensagens Note. As mensagens podem conter outras mensagens. No caso do campo Notes.notes
definido abaixo, indicamos que se trata de uma coleção de mensagens fornecendo a classe Note
como o primeiro parâmetro ao construtor messages.MessageField
(antes do número do campo):
class Notes(messages.Message): notes = messages.MessageField(Note, 1, repeated=True)
O campo Notes.notes
também é um campo repetido, conforme indicado pelo argumento de palavra-chave repeated=True
. Os valores dos campos repetidos têm de ser listas do tipo de campo da respetiva declaração. Neste caso, Notes.notes
tem de ser uma lista de instâncias de Note. As listas são criadas automaticamente e não podem ser atribuídas a Nenhum.
Por exemplo, veja como criar um 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
Implemente get_notes
Agora, podemos adicionar o método get_notes() à classe 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)