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 inoltre specificare il requisito per l'utilizzo dell'accesso nel file app.yaml. Al momento, la libreria Google Protocol RPC in App Engine non supporta altre metodologie di autenticazione.

La libreria Google Protocol RPC è un framework per l'implementazione di servizi RPC (Remote Procedure Call, chiamata di procedura remota) basati su HTTP. Un servizio RPC è una raccolta di tipi di messaggi e metodi remoti che offre un modo strutturato alle applicazioni esterne di interagire con le applicazioni web. Poiché puoi definire messaggi e servizi nel linguaggio di programmazione Python, è facile sviluppare servizi Protocol RPC, testarli e scalarli su App Engine.

Sebbene sia possibile utilizzare la libreria Google Protocol RPC 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 comunicazione server a lunga esecuzione

Puoi definire un servizio Google Protocol RPC in una singola classe Python contenente un numero illimitato 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 of Google Protocol RPC

Questa sezione presenta un esempio di una 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 mediante l'applicazione guestbook sviluppata in Python. Gli utenti possono visitare il guestbook (incluso anche come demo nell'SDK Python) online, scrivere e visualizzare le voci 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 spiega come utilizzare Google Protocol RPC per estendere la funzionalità del guestbook. Sta a te decidere come procedere. Ad esempio, potresti voler scrivere uno strumento che legga i messaggi pubblicati dagli utenti e crei un grafico con serie temporali dei post al giorno. La modalità di utilizzo di Protocol RPC dipende dall'app specifica; la questione importante è che Google Protocol RPC espande notevolmente ciò che puoi fare con i dati della tua applicazione.

Per iniziare, devi creare 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 delle applicazioni. Utilizzerai questo file per definire il nuovo servizio, che implementa due metodi: uno per la pubblicazione dei dati in remoto e l'altro per la ricezione dei dati in remoto.

Ora non devi aggiungere nulla a questo file, ma è in questo file 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.

Utilizzare 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 di base Message. quindi specifica gli attributi di 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 di saluto per archiviare un post nel datastore. Definiamo un messaggio che rappresenta una nota:

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. Nel definire la stringa, eseguiamo inoltre:

  • 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.
  • Imposta text come campo obbligatorio. I campi sono facoltativi per impostazione predefinita, 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 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 valori in 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 quanto segue
Hello guestbook!
Good-bye guestbook!

Definizione di un servizio

Un servizio è una definizione della classe che eredita dalla classe base Service. I metodi remoti di un servizio sono indicati utilizzando il decorator 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 utilizza 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 Google Protocol RPC è dotata di 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 è considerato pubblicato.

Poiché Note.when è un campo facoltativo, potrebbe non essere stato impostato dal chiamante. In questo caso, il valore di when è impostato su None. 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 un'istanza 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. Puoi visualizzare la nota accedendo all'applicazione guestbook nel browser (http://localhost:8080/).

Aggiunta di campi dei messaggi

Ora che possiamo pubblicare messaggi su PostService, aggiungiamo un nuovo metodo per ricevere i 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. Definiscila al di sopra della 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 al PostService, questo 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 viene configurato esplicitamente, il valore predefinito di limit sarà 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 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 semplifica la conversione al suo nome o 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 di 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 di campo. I campi di enumerazione possono anche avere valori predefiniti.

Definizione del messaggio di risposta

Ora definiamo il messaggio di risposta get_notes(). La risposta deve essere una raccolta di messaggi 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 del campo):

class Notes(messages.Message):
    notes = messages.MessageField(Note, 1, repeated=True)

Anche il campo Notes.notes è 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 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

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)