Introducing WSGI: Python's Secret Web Weapon, Part Two
by James Gardner
|
Pages: 1, 2, 3
Middleware Chains
In the previous example, we saw how adding a single middleware component to an application gave it powerful new functionality. In fact, you don't have to stop at one middleware component. Since a combined middleware and application object is also a valid WSGI application, you can also wrap the combined application and middleware object in another middleware component. This leads to the idea of middleware chains, where you have a number of pieces of middleware between the server and the application. Below is an example using some fictional middleware components:
MyEnvironMiddleware(
MyStatusMiddleware(
SessionMiddleware(
application,
key='mysession',
secret='randomsecret',
)
),
'Some Configuration Option',
)
In situations such as the one above where you are using a number of middleware components, it is often more convenient to structure your code like this:
application = SessionMiddleware(application, key='mysession', secret='randomsecret',)
application = MyStatusMiddleware(application)
application = MyEnvironMiddleware(application, 'Some Configuration Option')
In a similar way, it's possible to create an entire web-framework stack just out of individual WSGI middleware components; indeed, the popular Pylons web framework, used to build production sites worldwide, already takes this approach.
Having a stack made entirely from WSGI middleware has a huge advantage: developers are free to pick and choose the components they need, or even to replace the parts of the stack they don't like by simply changing which middleware they use. If you've ever tried changing parts of the application stack in other framework architectures, you understand how hard it can sometimes be.
Error Handling
During development of an application, it's really useful to be able to debug errors. The first step is to display an error report. To do this, you can use the CgitbMiddleware middleware from the Paste project:
from paste.cgitb_catcher import CgitbMiddleware
application = CgitbMiddleware(application, {'debug':True})
Now if an exception is raised in your application code, a full error report--similar to one shown below--will be displayed.

Figure 1. CgitbMiddleware middleware in action
For production deployment, you'd want to disable this facility to prevent a visitor from accidentally being shown the values of important variables, such as passwords that might otherwise be displayed if an error occurred.
While it is undoubtedly useful to have a traceback report, it would be even more useful to be able to interactively debug each part of the call stack up to the point at which the error occurred by using a web-based command prompt. Such a solution already exists and can be added in exactly the same way:
from paste.evalexception import EvalException
application = EvalException(application)
The EvalException middleware will not work in a multiprocess environment, such as a WSGI application deployed as a CGI script, because the middleware must be able to store information about the error in memory. This is not possible if the whole application is restarted on each request. If you want to test the EvalException middleware, you could use this code:
def application(environ, start_response):
start_response('200 OK',[('Content-type','text/plain')])
response = []
variable1 = 'All local variables will be displayed'
response.append('Everything is going fine...\n')
raise Exception('Something went wrong!')
response.append("We won't get to here!")
return response
from paste.evalexception import EvalException
application = EvalException(application)
from wsgiref import simple_server
httpd = simple_server.WSGIServer(
('',8000),
simple_server.WSGIRequestHandler,
)
httpd.set_app(application)
httpd.serve_forever()
If you run the example above and visit http://localhost:8000, you will see the error report. Clicking on the
icon will give you the interactive debug prompt, and clicking on
will give you a representation of the code at the point that the error occurred. Try entering this at the prompt and press enter:
print "Hello World!"
You will see the Hello World! printed exactly as if it were entered at a normal Python prompt because the middleware acts like a full Python interpreter. You can even use the up and down arrows on your keyboard to scroll through the command history, just as you can at a real Python prompt.
The EvalException middleware also displays the values of local variables and has an extra data button that displays extra information about the environment. The screenshot below shows the middleware in action with the interactive prompt and local variables, including variable1, displayed:

Figure 2. EvalException middleware in action
Having this much power makes it much easier to debug applications, but makes it even more important that you disable debugging for production use so that malicious visitors can't execute destructive commands through your debug screen if an error occurred.
Once again, this example shows just how much useful functionality can be added to an application using a single middleware component.