Nota: se vuoi utilizzare l'autenticazione con Google Protocol RPC, puoi utilizzare l'autenticazione attualmente disponibile per le app App Engine nella console Google Cloud . Dovrai anche specificare il requisito di accesso all'utilizzo nel file
app.yaml. Al momento, altre metodologie di autenticazione non sono supportate dalla libreria RPC del protocollo Google in
App Engine.
La libreria Google Protocol RPC è un framework per l'implementazione di servizi di chiamata di procedura remota (RPC) basati su HTTP. Un servizio RPC è una raccolta di tipi di messaggi e metodi remoti che forniscono un modo strutturato per le applicazioni esterne di interagire con le applicazioni web. Poiché puoi definire messaggi e servizi nel linguaggio di programmazione Python, è facile sviluppare servizi RPC di protocollo, testarli e scalarli su App Engine.
Sebbene tu possa utilizzare la libreria RPC del protocollo Google per qualsiasi tipo di servizio RPC basato su HTTP, alcuni casi d'uso comuni includono:
- Pubblicazione di API web per l'utilizzo da parte di terze parti
- Creazione di backend Ajax strutturati
- Clonazione alla comunicazione con il server a esecuzione prolungata
Puoi definire un servizio RPC di Google Protocol in una singola classe Python che contiene un numero qualsiasi di metodi remoti dichiarati. Ogni metodo remoto accetta un insieme specifico di parametri come richiesta e restituisce una risposta specifica. Questi parametri di richiesta e risposta sono classi definite dall'utente note come messaggi.
Hello World di Google Protocol RPC
Questa sezione presenta un esempio di definizione di servizio molto semplice che riceve un messaggio da un client remoto. Il messaggio contiene il nome di un utente (HelloRequest.my_name) e restituisce un saluto per quella 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)])
Iniziare a utilizzare Google Protocol RPC
Questa sezione mostra come iniziare a utilizzare Google Protocol RPC utilizzando l'applicazione guestbook sviluppata in Python. Gli utenti possono visitare il guestbook (incluso anche come demo nell'SDK Python) online, scrivere voci e visualizzare quelle di tutti gli utenti. Gli utenti interagiscono direttamente con l'interfaccia, ma non esiste un modo per le applicazioni web di accedere facilmente a queste informazioni.
È qui che entra in gioco Protocol RPC. In questo tutorial, applicheremo Google Protocol RPC a questo guestbook di base, consentendo ad altre applicazioni web di accedere ai dati del guestbook. Questo tutorial tratta solo l'utilizzo di Google Protocol RPC per estendere la funzionalità del guestbook. Tocca a te decidere cosa fare dopo. Ad esempio, potresti scrivere uno strumento che legge i messaggi pubblicati dagli utenti e crea un grafico delle serie temporali dei post al giorno. Il modo in cui utilizzi Protocol RPC dipende dalla tua app specifica. L'importante è che Google Protocol RPC espande notevolmente le possibilità di utilizzo dei dati della tua applicazione.
Per iniziare, crea un file, postservice.py, che implementa metodi remoti per accedere ai dati nel datastore dell'applicazione guestbook.
Creazione del modulo PostService
Per iniziare a utilizzare Google Protocol RPC, il primo passaggio consiste nel creare un file denominato postservice.py nella directory dell'applicazione. Utilizzerai questo file per definire il nuovo servizio, che implementa due metodi: uno che pubblica i dati in remoto e un altro che li recupera in remoto.
Per ora non devi aggiungere nulla a questo file, ma è qui che inserirai tutto il codice definito nelle sezioni successive. Nella sezione successiva, creerai un messaggio che rappresenta una nota pubblicata nel datastore dell'applicazione guestbook.
Utilizzo di Messaggi
I messaggi sono il tipo di dati fondamentale utilizzato in Google Protocol RPC. I messaggi vengono definiti dichiarando una classe che eredita dalla classe base Message. Poi, specifica gli attributi della classe che corrispondono a ciascuno dei campi del messaggio.
Ad esempio, il servizio guestbook consente agli utenti di pubblicare una nota. Se non l'hai ancora fatto, crea un file denominato postservice.py nella directory dell'applicazione. PostService utilizza la classe Greeting per archiviare un post nel datastore. Definiamo un messaggio che rappresenti una nota di questo tipo:
from protorpc import messages class Note(messages.Message): text = messages.StringField(1, required=True) when = messages.IntegerField(2)
Il messaggio di nota è definito da due campi: text e when. Ogni campo ha un tipo specifico. Il campo di testo è una stringa Unicode che rappresenta il contenuto del post di un utente nella pagina del guestbook. Il campo when è un numero intero che rappresenta il timestamp del post. Nella definizione della stringa, abbiamo anche:
- Assegna a ogni campo un valore numerico univoco (
1pertexte2perwhen) che il protocollo di rete sottostante utilizza per identificare il campo. - Rendi
textun campo obbligatorio. I campi sono facoltativi per impostazione predefinita, ma puoi contrassegnarli come obbligatori impostandorequired=True. I messaggi devono essere inizializzati impostando un valore per i campi obbligatori. I metodi di servizio Google Protocol RPC accettano solo messaggi inizializzati correttamente.
Puoi impostare i valori per i campi utilizzando il costruttore della 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()))
Puoi anche leggere e impostare i valori di un messaggio come i normali valori degli attributi Python. Ad esempio, per modificare il messaggio:
print note_instance.text note_instance.text = u'Good-bye guestbook!' print note_instance.text
Hello guestbook! Good-bye guestbook!
Definizione di un servizio
Un servizio è una definizione di classe che eredita dalla classe base Service. I metodi remoti di un servizio sono indicati utilizzando il decoratore remote. Ogni metodo di un servizio accetta un singolo messaggio come parametro e restituisce un singolo messaggio come risposta.
Definiamo il primo metodo di PostService. Aggiungi quanto segue al tuo file 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()
Il decoratore remote accetta due parametri:
- Il tipo di richiesta previsto. Il metodo post_note() accetta un'istanza Note come tipo di richiesta.
- Il tipo di risposta previsto. La libreria RPC di Google Protocol include un tipo integrato chiamato VoidMessage (nel modulo
protorpc.message_types), definito come un messaggio senza campi. Ciò significa che il messaggio post_note() non restituisce nulla di utile al chiamante. Se viene restituito senza errori, il messaggio viene considerato pubblicato.
Poiché Note.when è un campo facoltativo, potrebbe non essere stato impostato dal chiamante. In questo caso, il valore di when è impostato su Nessuno. Quando Note.when è impostato su Nessuno, post_note() crea il timestamp utilizzando l'ora in cui ha ricevuto il messaggio.
Il messaggio di risposta viene istanziato dal metodo remoto e diventa il valore restituito del metodo remoto.
Registrazione del servizio
Puoi pubblicare il nuovo servizio come applicazione WSGI utilizzando la libreria protorpc.wsgi.service. Crea un nuovo file denominato services.py nella directory dell'applicazione e aggiungi il seguente codice per creare il servizio:
from protorpc.wsgi import service import postservice # Map the RPC service and path (/PostService) app = service.service_mappings([('/PostService', postservice.PostService)])
Ora aggiungi il seguente gestore al tuo file app.yaml sopra la voce catch-all esistente:
- url: /PostService.* script: services.app - url: .* script: guestbook.app
Testare il servizio dalla riga di comando
Ora che hai creato il servizio, puoi testarlo utilizzando curl o uno strumento a riga di comando simile.
# 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 risposta JSON vuota indica che la nota è stata pubblicata correttamente. Puoi visualizzare la nota andando all'applicazione Guestbook nel browser (http://localhost:8080/).
Aggiungere campi messaggio
Ora che possiamo pubblicare messaggi in PostService, aggiungiamo un nuovo metodo per ricevere messaggi da PostService. Innanzitutto, definiamo un messaggio di richiesta in postservice.py che definisce alcuni valori predefiniti e un nuovo campo enum che indica al server come ordinare le note nella risposta. Definisci sopra la classe PostService che hai definito in precedenza:
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 viene inviato a PostService, questo messaggio richiede un numero di note in una determinata data o prima di questa e in un ordine particolare. Il campo limit indica il numero massimo di note da recuperare. Se non viene impostato in modo esplicito, limit ha come valore predefinito 10 note (come indicato dall'argomento della parola chiave default=10).
Il campo dell'ordine introduce la classe EnumField, che attiva il tipo di campo enum quando il valore di un campo è limitato a un numero limitato di valori simbolici noti. In questo caso, enum indica al server come ordinare le note nella risposta. Per definire i valori enum, crea una sottoclasse della classe Enum. A ogni nome deve essere assegnato un numero univoco per il tipo. Ogni numero viene convertito in un'istanza del tipo di enumerazione e può essere accessibile dalla classe.
print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN.name, GetNotesRequest.Order.WHEN.number)
Ogni valore enum ha una caratteristica speciale che ne facilita la conversione nel nome o nel numero. Anziché accedere all'attributo nome e numero, converti ogni valore in una stringa o in un numero intero:
print 'Enum value Order.%s has number %d' % (GetNotesRequest.Order.WHEN, GetNotesRequest.Order.WHEN)
I campi enum vengono dichiarati in modo simile agli altri campi, tranne per il fatto che devono avere il tipo enum come primo parametro prima del numero del campo. I campi enum possono anche avere valori predefiniti.
Definire il messaggio di risposta
Ora definiamo il messaggio di risposta get_notes(). La risposta deve essere una raccolta di messaggi di nota. I messaggi possono contenere altri messaggi. Nel caso del campo Notes.notes definito di seguito, indichiamo che si tratta di una raccolta di messaggi fornendo la classe Note come primo parametro al costruttore messages.MessageField (prima del numero di campo):
class Notes(messages.Message): notes = messages.MessageField(Note, 1, repeated=True)
Il campo Notes.notes è anche un campo ripetuto, come indicato dall'argomento della parola chiave repeated=True. I valori dei campi ripetuti devono essere elenchi del tipo di campo della relativa dichiarazione. In questo caso, Notes.notes deve essere un elenco di istanze Note. Gli elenchi vengono creati automaticamente e non possono essere assegnati a Nessuno.
Ad esempio, ecco come creare un oggetto Note:
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
Implementare get_notes
Ora possiamo aggiungere il metodo get_notes() alla 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)