1.14.1.2 What Are Middleware Components?

Consider the slightly more complicated example below using the imaginary session handling module superSession:

#!/usr/bin/env python

import superSession
session = superSession.session()
print "Content-type: text/plain\n\n"
if session.has_key('visited'):
    print "You have already visited!"
else:
    session['visited'] = 1
    print "This is your first visit."

We create a session object and display a different string depending on whether or not the user has visited the site before. We could follow the approach above and create the following WSGI application to do the same thing:

def application(environ, start_response):
    import superSession
    session = superSession.session()
    if session.has_key('visited'):
        text = "You have already visited!"
    else:
        session['visited'] = 1
        text = "This is your first visit."
    start_response('200 OK', [('Content-type','text/plain')])
    return [text]

This would be perfectly good and work perfectly well. We could now refactor the code again:

def exampleApplication(environ, start_response):
    if environ['superSession'].has_key('visited'):
        text = "You have already visited!"
    else:
        environ['superSession']['visited'] = 1
        text = "This is your first visit."
    start_response('200 OK', [('Content-type','text/plain')])
    return [text]
    
def session(application):
    def app(environ, start_response):
        if "superSession" not in environ:
            import superSession
            environ["superSession"] = superSession.session() # Options would obviously need specifying
        return application(environ, start_response)
    return app
    
application = session(exampleApplication)

We have separated out the session code into a different function and added a key to the environ dictionary called "session" which contains the session object. Our exampleApplication then accesses the session object through the environ dictionary. Note how we have renamed our application function to exampleApplication and mapped the name application to the session(exampleApplication) object. The WSGI server will still be able to find a callable named application and so will still be able to run our application.

The session function is now what we call a middleware component as it sits in between the server and the application. It gives the application new functionality but the result of calling session(exampleApplication) is also just a WSGI application (because the combined object still conforms to the rules listed earlier) and so the server can still run the code.

The huge advantage of refactoring code in this way is that the session functionality can now easily be added to any WSGI application using our session function. By chaining together these middleware components (which do not even have to be based on the Web Modules) WSGI applications can gain an enormous amount of functionality for very little programming effort by using existing middleware components. This helps make code easy to maintain and offers a very flexible programming methodology.