A Raspberry Pi HTTP REST software stack

1   Introduction

In outline, here is our software stack:

  • Python
  • flask
  • flask-restful
  • uwsgi
  • nginx

I found some very useful help here: https://iotbytes.wordpress.com/python-flask-web-application-on-raspberry-pi-with-nginx-and-uwsgi/

2   Installation

2.1   nginx

Use apt-get or aptitude. E.g. with aptitude, you can do:

$ sudo aptitude install nginx

2.2   uwsgi

Use apt-get or aptitude. E.g. with aptitude, you can do:

$ sudo aptitude install uwsgi

2.3   flask and flask-restful

I suggest that you use a virtualenv. Do the following:

$ pip install virtualenv
$ cd where/you/want/your/virtualenvs
$ virtualenv -p /usr/bin/pyton3 restplatform3    # identify as Python 3
$ source restplatform3/bin/activate              # activate this virtual environment
$ pip install flask flask_restful

Notes:

  • After activating the virtual environment, you will be running the version of Python in that virtual environment whenever you run a script or you execute $ python. And, whenever you install a Python package, e.g. with pip, it will be installed inside the virtual environment directory that you have just created. You can check this (on Linux) by running:

    $ which python
    
  • The above commands create a virtual environment containing Python 3. If you prefer Python 2, omit the "-p" option and its argument.

You should also consider using pip to install a few useful things like ipython, lxml, etc.

4   Application development

Of course, you can write many different styles of applications that accept requests and deliver responses via HTTP. However, this current post focuses on developing an application that follows the REST application style. I've already written posts on how to do that. So, here I'm giving only a few brief notes on how to implement a simple REST application with Python and flask, specifically using the flask-restful package.

Our simple REST example application will respond to the following HTTP methods/requests:

  • GET -- Either (1) return a specific document when given an ID or (2) return a list of documents.
  • POST -- Create/add a new document (resource) to the collection.
  • PUT -- Update a document/resource that already in the collection given the ID of an existing document and its new content.

Here is the code: flaskrest_test05.py

Notes:

  • The flask-restful extension for flask enables us to (1) encode routing information and (2) write a class that implements methods that handle the various HTTP methods (GET, POST, PUT, DELETE, etc) that we want to handle.

5   Testing

You can use any of the following to drive tests for your HTTP REST application:

  • curl -- See:
  • httpie -- Note that although the project name is httpie, the command line tool is http. See:
  • Python and the requests module -- For information on requests, see: http://docs.python-requests.org/

5.1   curl

Examples:

get-one-document.sh:

#!/bin/bash
# get-one-document.sh
# Retrieve a single document from the database.
curl -v http://localhost:8001/documents/getdocument/$1

list-documents.sh:

#!/bin/bash
# list-documents.sh
# Retrieve a list of all existing documents.
http://localhost:8001/documents/getdocument

add-document.sh:

#!/bin/bash
# add-document.sh
# Create a new document; add it to the database.
curl -v -X POST http://localhost:8001/documents/adddocument -d "document=$1"

update-document.sh:

#!/bin/bash
# update-document.sh
# Update an existing document.
curl -v -X PUT http://localhost:8001/documents/updatedocument/$1 -d "document=$2"

delete-document.sh:

#!/bin/bash -x
# delete-document.sh
# Delete an existing document; remove it from the database.
http://localhost:8001/documents/delete/$1

5.2   httpie

httpie is a pleasant command line HTTP client and can be used as an alternative to curl. The actual command line name is "http". If you are a Python user, you can install it with pip:

$ pip install httpie

Or, on Linux, you can install it with apt-get or aptitude.

Here are several examples using httpie:

# Get a list of all documents
$ http GET http://localhost:8001/documents/getdocument
# Get one document by its ID
$ http GET http://localhost:8001/documents/getdocument/91a3b86fcbc59cc5f4a855d7894cb3be46546dccc18f5e833d24824ff4e4bf95

5.3   Python and requests

A test driver -- You can write a simple application using the requests module (which can be installed using pip). Here is some sample code to get you started.

rook_request.py:

#!/usr/bin/env python

from __future__ import print_function
import requests

def rook_request():
    #
    # Request HTML.
    response = requests.get('http://rook.local:5000')
    content = response.content.decode('utf-8')
    print('html content: {}'.format(content))
    print('-' * 60)
    #
    # Request JSON.
    headers = {'content-type': 'application/json'}
    response = requests.get('http://rook.local:5000', headers=headers)
    content = response.content.decode('utf-8')
    print('json content: {}'.format(content))

def main():
    rook_request()

if __name__ == '__main__':
    #import ipdb; ipdb.set_trace()
    main()

Timing -- You can get some estimates of response time using IPyton and the %timeit magic command. For example, the following uses the above Python code (in rook_request.py) and executes 10 loops with 1 call to rook_request.rook_request in each loop:

$ ipython
Python 3.6.0 (default, Dec 26 2016, 10:27:00)
Type "copyright", "credits" or "license" for more information.

IPython 5.1.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.
In [1]:
In [1]: import rook_request
In [2]: %timeit -n 10 -r 1 rook_request.rook_request()
    o
    o
    o
html content: <html>
<head>
<title>Flask test app on rook</title>
</head>
<body>
<h1>rook test 1</h1>
<p>Current time: Fri Mar 24 16:23:43 2017</p>
</body>
</html>

------------------------------------------------------------
json content: {"time": "Fri Mar 24 16:23:43 2017"}
10 loops, best of 1: 117 ms per loop
In [3]:

links