WSGI

web2py and WSGI have a love-hate relationship. Our perspective is that WSGI was developed as a protocol to connect web servers to web applications in a portable way, and we use it for that purpose. web2py at its core is a WSGI application: gluon.main.wsgibase. Some developers have pushed WSGI to its limits as a protocol for middleware communications and develop web applications as an onion with many layers (each layer being a WSGI middleware developed independently of the entire framework). web2py does not adopt this structure internally. This is because we feel the core functionality of a frameworks (handling cookies, session, errors, transactions, dispatching) can be better optimized for speed and security if they are handled by a single comprehensive layer.

Yet web2py allows you to use third party WSGI applications and middleware in three ways (and their combinations):

  • You can edit the file “wsgihandler.py” and include any third party WSGI middleware.
  • You can connect third party WSGI middleware to any specific action in your apps.
  • You can call a third party WSGI app from your actions.

The only limitation is that you cannot use third party middleware to replace core web2py functions.

External middleware

Consider the file “wsgihandler.py”:

  1. #...
  2. LOGGING = False
  3. #...
  4. if LOGGING:
  5. application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
  6. logfilename='httpserver.log',
  7. profilerfilename=None)
  8. else:
  9. application = gluon.main.wsgibase

When LOGGING is set to True, gluon.main.wsgibase is wrapped by the middleware function gluon.main.appfactory. It provides logging to the “httpserver.log” file. In a similar fashion you can add any third party middleware. We refer to the official WSGI documentation for more details.

Internal middleware

Given any action in your controllers (for example index) and any third party middleware application (for example MyMiddleware, which converts output to upper case), you can use a web2py decorator to apply the middleware to that action. Here is an example:

  1. class MyMiddleware:
  2. """converts output to upper case"""
  3. def __init__(self, app):
  4. self.app = app
  5. def __call__(self, environ, start_response):
  6. items = self.app(environ, start_response)
  7. return [item.upper() for item in items]
  8. @request.wsgi.middleware(MyMiddleware)
  9. def index():
  10. return 'hello world'

We cannot promise that all third party middleware will work with this mechanism.

Calling WSGI applications

It is easy to call WSGI app from a web2py action. Here is an example:

  1. def test_wsgi_app(environ, start_response):
  2. """this is a test WSGI app"""
  3. status = '200 OK'
  4. response_headers = [('Content-type', 'text/plain'),
  5. ('Content-Length', '13')]
  6. start_response(status, response_headers)
  7. return ['hello world!\n']
  8. def index():
  9. """a test action that calls the previous app and escapes output"""
  10. items = test_wsgi_app(request.wsgi.environ,
  11. request.wsgi.start_response)
  12. for item in items:
  13. response.write(item, escape=False)
  14. return response.body.getvalue()

In this case, the index action calls test_wsgi_app and verbatim copies the returned value to page output. Notice that index is not itself a WSGI app and it must use the normal web2py API (such as response.write to write to the socket).