Web Development

RESTful Web Service in Go Powered by the Google App Engine

By Alexander Demin, August 28, 2012

In this installment of our Go tutorial, Go's support for REST-based Web services and cloud computing make quick work of useful document analysis project.

In the next lines of code, I iterate over the all found strings and check
whether the current one looks as a batch number. If yes, I take it along with
the next two and put them aside as a record to the final table. If a record
with the same ID already exists, we append the current pair to the end of the
list (remember, one batch number can occur more once in the file, and we have
to collect them all).

Now, I have the LoadBatchTable() function returning the
contents of the PDF as a Go map. It is time to think about storing this map to
Memcache and creating the ability to read it back. In the next lines, I
introduce another structure called Table. It contains a timestamp
of when the record was created (an UpdateTime field) and a map
with the information from the PDF file (a Batches field).

type Table struct {
UpdateTime string
Batches BatchTable
}

If you are not familiar with memory management in Go, it is important to
emphasize some key principles. First, Go is a garbage-collected language, so
we don't need to worry about freeing memory. Second, Go has pointers. But
pointers in Go are much safer than in C or C++ because they don't provide
pointer arithmetic, so the pointer in Go is more like references in C++.
Moreover, Go can implicitly dereference pointers when you need to access
fields of the pointer's underlying structure. For example, if you have a
pointer of the type BatchTable declared as p
*BatchTable, you can either access UpdateTime via
p->UpdateTime or p.UpdateTime. It offers some
syntactic comfort. Third, Go can pass objects of any type either by pointer (that is, reference) or
by value, but some built-in types (for instance, maps and slices) are always
passed by pointer. For example, a variable of the type BatchTable
is always passed by pointer, but of the type Table, by default by
value. That is why in the next lines of code, we declare a type of the
function return value as a pointer to void deep copying.

The next lines also contain a function reading the table from Memcache.
Memcache API allows putting values of either the string type or arbitrary
compound types into it. We will use the compound type Table. Go
has a built-in mechanism called "Gob" for serializing any variable into an
array of bytes. In the next lines, we ask Memcache to read a record named
table and de-serialize it as avariable of the Table
type. If there is no such record, we assume that Memcache has purged it or
nobody read the table since the service started, and we load up the table from
the network.

At this point, I am pretty much done with the core functionality.I am able to
load and parse the PDF, then to store our data structure back to the GAE
cloud via Memcache.

For the sake convenient debugging, let me implement one more thing. I should
have the ability to print out the entire table of batches. With a text template
library available in Go, the implementation of such functionality takes a
couple lines of code. I define a template of an HTML page representing a
table. To understand template syntax, you basically need to know only two
things: placeholders and clauses. The placeholders are represented as
{{.PlaceHolderName}}. Placeholders will be replaced by a variable (or a
field) from the template data source named PlaceHolderName. There
is also is a range clause allowing traversal of a map or an array.

Now let's implement a function printing the table via this template. In the
following lines, Iprepare a data source. Note that, despite its native
compiling nature, the Go language has a very powerful introspection mechanism.
This allows the text template engine to be flexible in terms the data source
type. For example, if the data source is a map, the map keys will be become
the placeholder names, but if the data source is a structure (as in our case),
the template engine will automatically discover the field names and use them
as the placeholders. I also parse and execute the template. The output will
be sent to the outgoing stream represented by the w variable.

We are approaching the finale. The function GetBatch()
implements a data retrieval for a given batch number. First, it reads the
table from Memcache, checks whether it is time to refresh the data (our
obsolescence time is one hour), and finally gets the record from the
table.

Now, I'll implement our last HTTP handle for the /batch/
function, and I extract the requested batch number from the URL. Then, after
retrieving the batch information, I either report that such a batch doesn't
exist or iterate over all the records belonging to the given batch and print
them all.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!