Routing order in aiohttp library in Python

Routing order in aiohttp library in Python



08.07.2020

In the Grandmetric development team, we regularly use the aiohttp library as our asynchronous HTTP client/server for Python. This D&C shows how to use and register request handlers in the aiohttp application.

In particular, we would like to focus on paths order. That’s why you might find other solutions simplified for the sake of our example.

Let’s consider the oversimplified aiohttp server, which has two endpoints:

  • /names/all – returns all names stored in the database
  • /names/{id} – returns a name with given id

We can mock the data using simple Python’s dict:

data = [
    {"name": "Andy"},
    {"name": "Bob"},
    {"name": "Charlie"},
    {"name": "Duncan"},
]

Using request handlers

To handle each of the endpoints, we can use one of the two methods listed below.

First method

import json
from aiohttp import web
async def get_single(request):
    instance_id = int(request.match_info['id'])
    return web.Response(body=json.dumps(data[instance_id]), content_type="application/json")

Second method

import json
 import json  from aiohttp import web
async def get_all(request): return web.Response(body=json.dumps(data), content_type="application/json")

Registering request handlers

After that, we can simply register endpoints and run the application:

app = web.Application()

app.add_routes([web.get('/names/all', get_all)])
app.add_routes([web.get('/names/{id}', get_single)])

web.run_app(app)

This is where we might consider two variants.

  1. registering /names/{id} as the first route
  2. registering /names/all as the first route

It is important to notice that the first pattern that matches the path will be used to handle a particular request.

In the first scenario /names/{id} pattern will match requests like:

/names/0
/names/2
/names/any_string
/names/all

As you can see, it catches everything, so it shadows the second route i.e. /names/all, and its handler will never be reached.

The second scenario, when /names/all comes first solves this issue as it matches only literal /names/all, and other paths are matched by the second route.

Of course, this is not the only solution to this problem, as in this particular case, we could register the path to match only digits as follows:

app.add_routes([web.get('/names/{id:\d+}', get_single)])

Define paths as narrow as possible

As you could see in the example above, it’s important which order of registering paths in the aiohttp library you choose. The best way to avoid issues related to this is to define the path as narrow as possible, e.g., by adding regex as an identifier.

Take a look at this complete GIST example and play around with the order of the routes.

Author

Mateusz Buczkowski

Mateusz Buczkowski received his M.Sc. degree from Poznan University of Technology in 2012. Since then he is employed at the Chair of Telecommunication Systems and Optoelectronics in the Faculty of Electronics and Telecommunications as a teaching assistant. He is pursuing his PhD in field of image processing. His research interest cover the wide spectrum of image and video processing. In particular he is interested in image quality assessment, which is his PhD topic. As a R&D engineer he took part in two FP7 EU projects, namely 5GNOW and SOLDER, where he worked on solutions that can be used in 5-th Generation wireless networks, which is to come in 2020. In Grandmetric he is involved in wireless systems research.

Leave a Reply

Your email address will not be published. Required fields are marked *


 

Newsletter