Google Protocol RPC Library Overview

Note:If you wish to use authentication with Google Protocol RPC, you can use the authentication currently available for App Engine apps in the Google Cloud console. You'll also need to specify the use login requirement in your app.yaml file. Other auth methodologies are not currently supported by the Google Protocol RPC library within App Engine.

The Google Protocol RPC library is a framework for implementing HTTP-based remote procedure call (RPC) services. An RPC service is a collection of message types and remote methods that provide a structured way for external applications to interact with web applications. Because you can define messages and services in the Python programming language, it's easy to develop Protocol RPC services, test those services, and scale them on App Engine.

While you can use the Google Protocol RPC library for any kind of HTTP-based RPC service, some common use cases include:

  • Publishing web APIs for use by third parties
  • Creating structured Ajax backends
  • Cloning to long-running server communication

You can define a Google Protocol RPC service in a single Python class that contains any number of declared remote methods. Each remote method accepts a specific set of parameters as a request and returns a specific response. These request and response parameters are user-defined classes known as messages.

The Hello World of Google Protocol RPC

This section presents an example of a very simple service definition that receives a message from a remote client. The message contains a user's name ( HelloRequest.my_name ) and sends back a greeting for that person ( 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 
 )]) 

Getting Started with Google Protocol RPC

This section demonstrates how to get started with Google Protocol RPC using the guestbook application developed in the Python . Users can visit the guestbook (also included as a demo in the Python SDK) online, write entries, and view entries from all users. Users interact with the interface directly, but there is no way for web applications to easily access that information.

That's where Protocol RPC comes in. In this tutorial, we'll apply Google Protocol RPC to this basic guestbook, enabling other web applications to access the guestbook's data. This tutorial only covers using Google Protocol RPC to extend the guestbook functionality; it's up to you what to do next. For example, you might want to write a tool that reads the messages posted by users and makes a time-series graph of posts per day. How you use Protocol RPC depends on your specific app; the important point is that Google Protocol RPC greatly expands what you can do with your application's data.

To begin, you'll create a file, postservice.py , which implements remote methods to access data in the guestbook application's datastore.

Creating the PostService Module

The first step to get started with Google Protocol RPC is to create a file called postservice.py in your application directory. You'll use this file to define the new service, which implements two methods—one that remotely posts data and another that remotely gets data.

You don't need to add anything to this file now—but this is the file where you'll put all the code defined in the subsequent sections. In the next section, you'll create a message that represents a note posted to the guestbook application's datastore.

Working with Messages

Messages are the fundamental data type used in Google Protocol RPC. Messages are defined by declaring a class that inherits from the Message base class. Then you specify class attributes that correspond to each of the message's fields.

For example, the guestbook service allows users to post a note. If you haven't done so already, create a file called postservice.py in your application directory. The PostService uses the Greeting class to store a post in the datastore. Let's define a message that represents such a note:

 from 
  
 protorpc 
  
 import 
 messages 
 class 
  
 Note 
 ( 
 messages 
 . 
 Message 
 ): 
 text 
 = 
 messages 
 . 
 StringField 
 ( 
 1 
 , 
 required 
 = 
 True 
 ) 
 when 
 = 
 messages 
 . 
 IntegerField 
 ( 
 2 
 ) 

The note message is defined by two fields, text and when . Each field has a specific type. The text field is a unicode string representing the content of a user's post to the guestbook page. The when field is an integer representing the post's timestamp. In defining the string, we also:

  • Give each field a unique numerical value ( 1 for text and 2 for when ) that the underlying network protocol uses to identify the field.
  • Make text a required field. Fields are optional by default, you can mark them as required by setting required=True . Messages must be initialized by setting required fields to a value. Google Protocol RPC service methods accept only properly initialized messages.

You can set values for the fields using the constructor of the Note class:

 # Import the standard time 
Python library to handle the timestamp. 
 import 
  
 time 
 note_instance 
 = 
 Note 
 ( 
 text 
 = 
 u 
 'Hello guestbook!' 
 , 
 when 
 = 
 int 
 ( 
 time 
 . 
 time 
 ())) 

You can also read and set values on a message like normal Python attribute values. For example, to change the message:

 print 
 note_instance 
 . 
 text 
 note_instance 
 . 
 text 
 = 
 u 
 'Good-bye guestbook!' 
 print 
 note_instance 
 . 
 text 
which outputs the following
Hello guestbook!
Good-bye guestbook!

Defining a Service

A service is a class definition that inherits from the Service base-class. Remote methods of a service are indicated by using the remote decorator. Every method of a service accepts a single message as its parameter and returns a single message as its response.

Let's define the first method of the PostService. Add the following to your postservice.py file:

 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 
 () 

The remote decorator takes two parameters:

  • The expected request type. The post_note() method accepts a Note instance as its request type.
  • The expected response type. The Google Protocol RPC library comes with a built-in type called a VoidMessage (in the protorpc.message_types module), which is defined as a message with no fields. This means that the post_note() message does not return anything useful to its caller. If it returns without error, the message is considered to have been posted.

Since Note.when is an optional field, it may not have been set by the caller. When this happens, the value of when is set to None. When Note.when is set to None, post_note() creates the timestamp using the time it received the message.

The response message is instantiated by the remote method and becomes the remote method's return value.

Registering the Service

You can publish your new service as a WSGI application using the protorpc.wsgi.service library. Create a new file called services.py in your application directory and add the following code to create your service:

 from 
  
 protorpc.wsgi 
  
 import 
 service 
 import 
  
 postservice 
 # Map the RPC service and path (/PostService) 
 app 
 = 
 service 
 . 
 service_mappings 
 ([( 
 '/PostService' 
 , 
 postservice 
 . 
 PostService 
 )]) 

Now, add the following handler to your app.yaml file above the existing catch-all entry:

 - 
 url 
 : 
 / 
 PostService 
 .* 
 script 
 : 
 services 
 . 
 app 
 - 
 url 
 : 
 .* 
 script 
 : 
 guestbook 
 . 
 app 

Testing the Service from the Command Line

Now that you've created the service, you can test it using curl or a similar command-line tool.

 # 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 

An empty JSON response indicates that the note posted successfully. You can see the note by going to your guestbook application in your browser (http://localhost:8080/).

Adding Message Fields

Now that we can post messages to the PostService, let's add a new method to get messages from the PostService. First, we'll define a request message in postservice.py that defines some defaults and a new enum field that tells the server how to order notes in the response. Define it above the PostService class that you defined earlier:

 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 
 ) 

When sent to the PostService, this message requests a number of notes on or before a certain date and in a particular order. The limit field indicates the maximum number of notes to fetch. If not explicitly set, limit defaults to 10 notes (as indicated by the default=10 keyword argument).

The order field introduces the EnumField class, which enables the enum field type when the value of a field is restricted to a limited number of known symbolic values. In this case, the enum indicates to the server how to order notes in the response. To define the enum values, create a sub-class of the Enum class. Each name must be assigned a unique number for the type. Each number is converted to an instance of the enum type and can be accessed from the class.

 print 
 'Enum value Order. 
 %s 
 has number 
 %d 
 ' 
 % 
 ( 
 GetNotesRequest 
 . 
 Order 
 . 
 WHEN 
 . 
 name 
 , 
 GetNotesRequest 
 . 
 Order 
 . 
 WHEN 
 . 
 number 
 ) 

Each enum value has a special characteristic that makes it easy to convert to their name or their number. Instead of accessing the name and number attribute, just convert each value to a string or an integer:

 print 
 'Enum value Order. 
 %s 
 has number 
 %d 
 ' 
 % 
 ( 
 GetNotesRequest 
 . 
 Order 
 . 
 WHEN 
 , 
 GetNotesRequest 
 . 
 Order 
 . 
 WHEN 
 ) 

Enum fields are declared similarly to other fields except they must have the enum type as its first parameter before the field number. Enum fields can also have default values.

Defining the Response Message

Now let's define the get_notes() response message. The response needs to be a collection of Note messages. Messages can contain other messages. In the case of the Notes.notes field defined below, we indicate that it is a collection of messages by providing the Note class as the first parameter to the messages.MessageField constructor (before the field number):

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

The Notes.notes field is also a repeated field as indicated by the repeated=True keyword argument. Values of repeated fields must be lists of the field type of their declaration. In this case, Notes.notes must be a list of Note instances. Lists are automatically created and cannot be assigned to None.

For example, here is how to create a Notes object:

 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 

Implement get_notes

Now we can add the get_notes() method to the PostService class:

 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 
 ) 
Create a Mobile Website
View Site in Mobile | Classic
Share by: