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”:
#...
LOGGING = False
#...
if LOGGING:
application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase,
logfilename='httpserver.log',
profilerfilename=None)
else:
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:
class MyMiddleware:
"""converts output to upper case"""
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
items = self.app(environ, start_response)
return [item.upper() for item in items]
@request.wsgi.middleware(MyMiddleware)
def index():
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:
def test_wsgi_app(environ, start_response):
"""this is a test WSGI app"""
status = '200 OK'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', '13')]
start_response(status, response_headers)
return ['hello world!\n']
def index():
"""a test action that calls the previous app and escapes output"""
items = test_wsgi_app(request.wsgi.environ,
request.wsgi.start_response)
for item in items:
response.write(item, escape=False)
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).