I agree that our last week "Hello World" application was not very impressive.
In fact I don't think any "Hello World" can be impressive at all, unless it has some mighty not-hello-world-at-all features.
Let's sum up what we have so far. We have a python script, that spawns a webserver on port 2222 and answers HTTP requests saying hello.
For a ten line script, it's not so bad, but still hello-word-ish.
WSGI introduce the concept of "middlewares" (Les wares du milieu).
It is, like WSGI, a very simple concept. As all your WSGI applications have the same prototype, it's very easy to create decorators that would be reusable WSGI application enhancers.
The picture on right, which comes from the Pylons framework documentation, explain it graphically. Of course the layers presented here are only an example from the said framework, and your setup is only limited by your immagination.
The most amazing middleware ever: ajax interactive debugger
Let me introduce a new friend, weberror.
At a first glance, weberror interface may look like any other stack trace dumper from <insert-your-favorite-language-or-framework-here>.
But if you take a few seconds to explore it, you will get why it's 100 times more powerfull. The huge difference is that you can expand a frame (using the little plus icon) to get an interactive python interpreter in the context of the stack frame.
This is great, but looks like a hell to integrate in some home-made application... Actually it is not! Weberror contains a WSGI middleware that will enpower any WSGI compliant application with its features. Let's add it to our last week's application.
from webob import Response as HttpResponse from webob.dec import wsgify from weberror.evalexception import EvalException as AjaxDebuggerMiddleware @wsgify def application(request): if request.path_info == '/error': raise Exception('Guess what, an error happened!') return HttpResponse('Hello, debuggable world at %s.' % request.path_info) application = AjaxDebuggerMiddleware(application) if __name__ == '__main__': from gevent import wsgi wsgi.WSGIServer(('', 2222), application, spawn=None).serve_forever()
Note that the feature costs 2 line of python (one import, and the decoration of our original app). We added 2 more lines to raise an error if the URL is /error.
To avoid the usual mistake of running the app bundled with the debugger unless we really want to, let's add a --debug command line option that enables it.
from webob import Response as HttpResponse from webob.dec import wsgify @wsgify def application(request): if request.path_info == '/error': raise Exception('Guess what, an error happened!') return HttpResponse('Hello, debuggable world at %s.' % request.path_info) if __name__ == '__main__': from gevent import wsgi from optparse import OptionParser parser = OptionParser() parser.add_option("-D", "--debug", dest="debug", action="store_true", default=False, help="activate the interactive web debugger") options, args = parser.parse_args() if options.debug: from weberror.evalexception import EvalException as AjaxDebuggerMiddleware application = AjaxDebuggerMiddleware(application) wsgi.WSGIServer(('', 2222), application, spawn=None).serve_forever()
You can now try the new features.
$ python middleware2.py --help Usage: middleware2.py [options] Options: -h, --help show this help message and exit -D, --debug activate the interactive web debugger
Ok, and now what ?
Now you've seen one of the most stunning middleware that exists, let's think of other cool usage our framework may need. Note that I'm not claiming all of those features are actually good ideas to be implemented as middleware, but they are implementable this way.
- Middlewares can intercept requests ...
- One can route requests between several WSGI applications (subdomains ?)
- One can handle HTTP cache using HTTP headers. If a cached version is there, the request won't go to the next WSGI layer.
- Middlewares can intercept outgoing stuff ...
- One can consider logging unhandled errors on a prod server, or even sending mails about them.
- One can look at the request Accept header and provide content type adapters for unacceptable types. You don't know about XML ? Let me present you an HTML view of this file.
- Middlewares can put additionnal informations in the WSGI environment dict.
- One can handle session storage/loading.
- And much more ...
The drawback of using middlewares is that it adds complexity to your application factory (the callable responsible for building the wsgi app).
As soon as you want to switch components painlessly, middlewares are starting to get in your way. You probably won't ever change the debugger, only enable or disable it, so the middleware solution is fine.
But what about session handling for example ? Or cache backends ? It's completely possible to use middlewares, and you may have a look at beaker that contains middlewares for caching and sessions.
However, we'll add those features using a much more flexible technique very soon.