negotiation - Content negotiation

Content negotiation is the process of selecting an appropriate representation (e.g. JSON, HTML, etc.) to return to a client based on the client’s and/or server’s preferences.

If no custom renderers are supplied, this plugin will render responses to JSON.

import asyncio

from aiohttp import web
from aiohttp_utils import negotiation, Response

async def handler(request):
    return Response({'message': "Let's negotiate"})

app = web.Application()
app.router.add_route('GET', '/', handler)

# No configuration: renders to JSON by default
negotiation.setup(app)

We can consume the app with httpie.

$ pip install httpie
$ http :5000/
HTTP/1.1 200 OK
CONNECTION: keep-alive
CONTENT-LENGTH: 30
CONTENT-TYPE: application/json
DATE: Thu, 22 Oct 2015 06:03:39 GMT
SERVER: Python/3.5 aiohttp/0.17.4

{
    "message": "Let's negotiate"
}

Note

Handlers MUST return an aiohttp_utils.negotiation.Response (which can be imported from the top-level aiohttp_utils module) for data to be properly negotiated. aiohttp_utils.negotiation.Response is the same as aiohttp.web.Response, except that its first argument is data, the data to be negotiated.

Customizing negotiation

Renderers are just callables that receive a request and the data to render.

Renderers can return either the rendered representation of the data or a Response.

Example:

# A simple text renderer
def render_text(request, data):
    return data.encode(request.charset)

# OR, if you need to parametrize your renderer, you can use a class

class TextRenderer:
    def __init__(self, charset):
        self.charset = charset

    def __call__(self, request, data):
        return data.encode(self.charset)

render_text_utf8 = TextRenderer('utf-8')
render_text_utf16 = TextRenderer('utf-16')

You can then pass your renderers to setup with a corresponding media type.

from collections import OrderedDict
from aiohttp_utils import negotiation

negotiation.setup(app, renderers=OrderedDict([
    ('text/plain', render_text),
    ('application/json', negotiation.render_json),
]))

Note

We use an OrderedDict of renderers because priority is given to the first specified renderer when the client passes an unsupported media type.

By default, rendering the value returned by a handler according to content negotiation will only occur if this value is considered True. If you want to enforce the rendering whatever the boolean interpretation of the returned value you can set the force_rendering flag:

from collections import OrderedDict
from aiohttp_utils import negotiation

negotiation.setup(app, force_rendering=True,
                  renderers=OrderedDict([
    ('application/json', negotiation.render_json),
]))
aiohttp_utils.negotiation.setup(app: aiohttp.web_app.Application, *, negotiator: callable = <function select_renderer>, renderers: collections.OrderedDict = {'application/json': <JSONRenderer()>}, force_negotiation: bool = True, force_rendering: bool = False)[source]

Set up the negotiation middleware. Reads configuration from app['AIOHTTP_UTILS'].

Parameters:
  • app (Application) – Application to set up.
  • negotiator – Function that selects a renderer given a request, a dict of renderers, and a force parameter (whether to return a renderer even if the client passes an unsupported media type).
  • renderers (OrderedDict) – Mapping of mediatypes to callable renderers.
  • force_negotiation (bool) – Whether to return a renderer even if the client passes an unsupported media type).
  • force_rendering (bool) – Whether to enforce rendering the result even if it considered False.
aiohttp_utils.negotiation.negotiation_middleware(renderers={'application/json': <JSONRenderer()>}, negotiator=<function select_renderer>, force_negotiation=True, force_rendering=False)[source]

Middleware which selects a renderer for a given request then renders a handler’s data to a aiohttp.web.Response.

class aiohttp_utils.negotiation.Response(data=None, *args, **kwargs)[source]

Same as aiohttp.web.Response, except that the constructor takes a data argument, which is the data to be negotiated by the negotiation_middleware.

aiohttp_utils.negotiation.select_renderer(request: aiohttp.web_request.Request, renderers: collections.OrderedDict, force=True)[source]

Given a request, a list of renderers, and the force configuration option, return a two-tuple of: (media type, render callable). Uses mimeparse to find the best media type match from the ACCEPT header.

class aiohttp_utils.negotiation.JSONRenderer[source]

Callable object which renders to JSON.

json_module = <module 'json' from '/home/docs/.pyenv/versions/3.7.9/lib/python3.7/json/__init__.py'>
aiohttp_utils.negotiation.render_json = <JSONRenderer()>

Render data to JSON. Singleton JSONRenderer. This can be passed to the RENDERERS configuration option, e.g. ('application/json', render_json).