Search code examples
pythonaiohttp

how does python aiohttp.web middleware work


according to the documentation

The handler passed in to a middleware factory is the handler returned by the next middleware factory. The last middleware factory always receives the request handler selected by the router itself (by UrlDispatcher.resolve()).

I thought UrlDispatcher.resolve() will return the registered handler that I assigned so I wrote this code. Based on my understanding when the page 127.0.0.1:9000 is accessed the index handler will be used as handler for m1

import logging;
import asyncio
from aiohttp import web

logging.basicConfig(level=logging.INFO)

@asyncio.coroutine
def m1(app,handler):
    def log(request):
        r = yield from handler(request)
        logging.info(str(r))

@asyncio.coroutine
def index(request):
    return web.Response(body=b'<h1>Aswesome</h1>')

@asyncio.coroutine
def names(request):
    return web.Response(text='<h1>%s</h1>' % request.match_info['name'])

@asyncio.coroutine
def init(loop):
    app = web.Application(loop=loop, middlewares=[
        m1
    ])
    app.router.add_route('GET','/',index)
    app.router.add_route('GET','/{name:\w+}',names)
    srv = yield from loop.create_server(app.make_handler(),'127.0.0.1',9000)
    logging.info('server started at http://127.0.0.1:9000')
    return srv

loop = asyncio.get_event_loop()
loop.run_until_complete(init(loop))
loop.run_forever()

When I ran the code and accessed the server at 127.0.0.1:9000 I got

  File "/home/os/Documents/Python/web/lib/python3.5/site-packages/aiohttp/web.py", line 90, in handle_request
    resp = yield from handler(request)
TypeError: 'NoneType' object is not callable

Which seems to me like a NoneType was passed into the m1 middleware as handler


Solution

  • Your middleware returns nothing, but middleware handler log should be returned. I added a line to your code, which returns log.

    @asyncio.coroutine
    def m1(app,handler):
        def log(request):
            r = yield from handler(request)
            logging.info(str(r))
        return log  # changed
    

    See documentation regarding middlewares for more details. Also, it worth to look into Python 3.5, which provides async/await syntax for work with awaitable objects aka coroutines. Usage of the syntax is suggested by the contributors of aiohttp.

    See official PEP-492 or slides from the talk from Igor Davydenko with the introduction into the new syntax.