Panoramica della libreria Google Protocol RPC

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 (1 per text e 2 per when) che il protocollo di rete sottostante utilizza per identificare il campo.
  • Rendi text un campo obbligatorio. I campi sono facoltativi per impostazione predefinita, ma puoi contrassegnarli come obbligatori impostando required=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
che restituisce il seguente output
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)