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 ai dati nel tuo
app.yaml
. Altre metodologie di autenticazione non sono attualmente 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 consentire alle 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 di Google Protocol 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 per la comunicazione con il server a lungo termine
Puoi definire un servizio RPC di protocollo Google in un'unica classe Python contenente 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 chiamate 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 invia 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)])
Introduzione a Google Protocol RPC
Questa sezione mostra come iniziare a utilizzare Google Protocol RPC con l'applicazione guestbook sviluppata in Python. Gli utenti possono visitare il guestbook (incluso come demo nell'SDK Python) online, scrivere voci e visualizzare quelle di tutti gli utenti. Gli utenti interagiscono direttamente con l'interfaccia, ma le applicazioni web non sono in grado di accedere facilmente a tali informazioni.
È qui che entra in gioco Protocol RPC. In questo tutorial applicheremo la RPC del protocollo Google a questo guestbook di base, consentendo ad altre applicazioni web di accedere ai dati del guestbook. Questo tutorial riguarda solo l'utilizzo della RPC del protocollo Google per estendere la funzionalità guestbook; spetta a te decidere cosa fare. Ad esempio, potresti voler scrivere uno strumento che legga i messaggi pubblicati dagli utenti e crei un grafico di una serie temporale dei post al giorno. La modalità di utilizzo dell'RPC protocollo dipende dall'app specifica. il punto importante è che Google Protocol RPC espande notevolmente ciò che puoi fare con i 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
Il primo passaggio per iniziare a utilizzare Google Protocol RPC è 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 da remoto e un altro che li recupera da remoto.
Al momento 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 data store dell'applicazione guestbook.
Utilizzare Messaggi
I messaggi sono il tipo di dati fondamentale utilizzato nel protocollo RPC di Google. I messaggi vengono definiti mediante la dichiarazione di una classe che eredita dalla classe di base Message. Poi specifichi gli attributi della classe corrispondenti a ciascuno dei campi del messaggio.
Ad esempio, il servizio di 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 della 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, occorre anche:
- Assegna a ogni campo un valore numerico univoco (
1
pertext
e2
perwhen
) utilizzato dal protocollo di rete sottostante per identificare il campo. - Rendi
text
un 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 del servizio RPC del protocollo Google accettano solo messaggi inizializzati correttamente.
Puoi impostare i valori per i campi utilizzando il costruttore della classe Nota:
# 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 su 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 di 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 decorator remote
richiede due parametri:
- Il tipo di richiesta previsto. Il metodo post_note() accetta un'istanza di Note come tipo di richiesta.
- Il tipo di risposta previsto. La libreria RPC del protocollo Google è dotata di un tipo integrato chiamato VoidMessage (nel modulo
protorpc.message_types
), che è 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 dall'utente che ha chiamato. In questo caso, il valore di when
è impostato su Nessuno. Se Note.when
è impostato su None, post_note() crea il timestamp utilizzando l'ora in cui ha ricevuto il messaggio.
Il messaggio di risposta viene creato 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 file app.yaml
sopra la voce generica esistente:
- url: /PostService.* script: services.app - url: .* script: guestbook.app
Test del 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/).
Aggiunta di campi per i messaggi
Ora che possiamo pubblicare messaggi in PostService, aggiungiamo un nuovo metodo per ricevere messaggi da PostService. In primo luogo, indicheremo 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. Definiscila 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, il messaggio richiede una serie di note entro una determinata data e in un determinato ordine. Il campo limit
indica il numero massimo di note da recuperare. Se non impostato esplicitamente, il valore predefinito di limit
è 10 note (come indicato dall'argomento della parola chiave default=10
).
Il campo 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 dell'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 enum ed è 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 semplifica la conversione in nome o numero. Anziché accedere all'attributo name e number, basta convertire 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, ad eccezione del fatto che deve avere il tipo enum come primo parametro prima del numero di campo. Anche i campi enum possono avere valori predefiniti.
Definizione del messaggio di risposta
Definiamo ora 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 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 di Note. Le liste vengono create automaticamente e non possono essere assegnate a Nessuna.
Ad esempio, ecco come creare un oggetto 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
Implementa 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)