# Request
The Request
instance contains a lot of helpful information available on its parameters. Refer to the API documentation (opens new window) for full details.
# Body
# Context
# Request context
The request.ctx
object is your playground to store whatever information you need to about the request.
This is often used to store items like authenticated user details. We will get more into middleware later, but here is a simple example.
@app.on_request
async def run_before_handler(request):
request.ctx.user = await fetch_user_by_token(request.token)
@app.route('/hi')
async def hi_my_name_is(request):
return text("Hi, my name is {}".format(request.ctx.user.name))
A typical use case would be to store the user object acquired from database in an authentication middleware. Keys added are accessible to all later middleware as well as the handler over the duration of the request.
Custom context is reserved for applications and extensions. Sanic itself makes no use of it.
# Connection context
Often times your API will need to serve multiple concurrent (or consecutive) requests to the same client. This happens, for example, very often with progressive web apps that need to query multiple endpoints to get data.
The HTTP protocol calls for an easing of overhead time caused by the connection with the use of keep alive headers.
When multiple requests share a single connection, Sanic provides a context object to allow those requests to share state.
@app.on_request
async def increment_foo(request):
if not hasattr(request.conn_info.ctx, "foo"):
request.conn_info.ctx.foo = 0
request.conn_info.ctx.foo += 1
@app.get("/")
async def count_foo(request):
return text(f"request.conn_info.ctx.foo={request.conn_info.ctx.foo}")
$ curl localhost:8000 localhost:8000 localhost:8000
request.conn_info.ctx.foo=1
request.conn_info.ctx.foo=2
request.conn_info.ctx.foo=3
# Parameters
Values that are extracted from the path are injected into the handler as parameters, or more specifically as keyword arguments. There is much more detail about this in the Routing section.
@app.route('/tag/<tag>')
async def tag_handler(request, tag):
return text("Tag - {}".format(tag))
# Arguments
There are two attributes on the request
instance to get query parameters:
request.args
request.query_args
$ curl http://localhost:8000\?key1\=val1\&key2\=val2\&key1\=val3
>>> print(request.args)
{'key1': ['val1', 'val3'], 'key2': ['val2']}
>>> print(request.args.get("key1"))
val1
>>> print(request.args.getlist("key1"))
['val1', 'val3']
>>> print(request.query_args)
[('key1', 'val1'), ('key2', 'val2'), ('key1', 'val3')]
>>> print(request.query_string)
key1=val1&key2=val2&key1=val3
FYI
💡 The request.args
object is one of a few types that is a dictionary with each value being a list. This is because HTTP allows a single key to be reused to send multiple values.
Most of the time you will want to use the .get()
method to access the first element and not a list. If you do want a list of all items, you can use .getlist()
.
# Current request getter
Sometimes you may find that you need access to the current request in your application in a location where it is not accessible. A typical example might be in a logging
format. You can use Request.get_current()
to fetch the current request (if any).
import logging
from sanic import Request, Sanic, json
from sanic.exceptions import SanicException
from sanic.log import LOGGING_CONFIG_DEFAULTS
LOGGING_FORMAT = (
"%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: "
"%(request_id)s %(request)s %(message)s %(status)d %(byte)d"
)
old_factory = logging.getLogRecordFactory()
def record_factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
record.request_id = ""
try:
request = Request.get_current()
except SanicException:
...
else:
record.request_id = str(request.id)
return record
logging.setLogRecordFactory(record_factory)
LOGGING_CONFIG_DEFAULTS["formatters"]["access"]["format"] = LOGGING_FORMAT
app = Sanic("Example", log_config=LOGGING_CONFIG_DEFAULTS)
In this example, we are adding the request.id
to every access log message.
Added in v22.6