Middlewares#
A middleware is an object that wraps the original application, hence the name. A middle is called between the application and the server. It can modify the response or the environment or route requests to different application objects.
Middlewares and apps are agnostic to each other, so we can plumb any WSGI app to our middleware, and our middleware to any WSGI app. Middleware can be chained, allowing our response or request to go through multiple phases of processing.
Here is for example how the Django web framework chains multiple middlewares before calling the application:
$ django-admin startproject example
$ grep -c2 -ni middleware example/example/settings.py
grep -i middleware example/example/settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Django imports these middlewares from the their specified module and plumbs them one after another. Let's see how we can do that too.
Other frameworks use extentions
sometimes also called plugins
or
includes
. For example, the Flask framework really wraps the
Application
instance:
"""
Example how flask uses 3 middlewares each wrapping around the
application
"""
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.bcrypt import Bcrypt
from flask.ext.login import LoginManager
app = Flask(__name__)
bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
db = SQLAlchemy(app)
Bottle.py
has a similar approach:
import bottle
from bottle.ext import sqlite
app = bottle.Bottle()
plugin = sqlite.Plugin(dbfile='/tmp/test.db')
app.install(plugin)
@app.route('/show/:item')
def show(item, db):
row = db.execute('SELECT * from items where name=?', item).fetchone()
if row:
return template('showitem', page=row)
return HTTPError(404, "Page not found")
Bottle hides the fact that the app.install
command is wrapping your
application with a call plugin which takes place before your application
and after your application login. These actions could be for example opening
and closing connections to the database before and after the request if
needed.
A Simple WSGI Middleware#
As an example we show a simple WSGI middle which logs the environment dictionary to the console:
def log_environ(handler):
"""print the environment dictionary to the console"""
from pprint import pprint
def _inner(environ, start_function):
pprint(environ)
return handler(environ, start_function)
return _inner
# this will show "Hello World!" in your browser,
# and the environment in the console
app = log_environ(hello_world_app)
Exercise 2#
Implement your own middleware which capitalizes the response you original application return.
Exercise 3#
Implement your own middleware which reverses the response. Upon calling this middleware twice you should see the original response, e.g.:
def reverser(wsgiapp):
...
...
app = reverser(reverser(app)) # should return Hello WSGI!