sanic/docs/sanic/middleware.md

159 lines
4.6 KiB
Markdown
Raw Normal View History

2017-03-23 15:49:23 -04:00
# Middleware And Listeners
2016-10-14 04:51:08 -07:00
Middleware are functions which are executed before or after requests to the
server. They can be used to modify the *request to* or *response from*
user-defined handler functions.
2016-10-14 04:51:08 -07:00
2017-08-19 01:03:54 +08:00
Additionally, Sanic provides listeners which allow you to run code at various points of your application's lifecycle.
2017-03-23 15:49:23 -04:00
## Middleware
There are two types of middleware: request and response. Both are declared
using the `@app.middleware` decorator, with the decorator's parameter being a
string representing its type: `'request'` or `'response'`.
2016-10-14 04:51:08 -07:00
* Request middleware receives only the `request` as argument.
* Response middleware receives both the `request` and `response`.
2016-10-14 04:51:08 -07:00
The simplest middleware doesn't modify the request or response at all:
2016-10-14 04:58:09 -07:00
2018-10-26 11:29:53 +03:00
```
2016-10-14 04:58:09 -07:00
@app.middleware('request')
async def print_on_request(request):
print("I print when a request is received by the server")
2016-10-14 04:51:08 -07:00
@app.middleware('response')
async def print_on_response(request, response):
print("I print when a response is returned by the server")
```
## Modifying the request or response
Middleware can modify the request or response parameter it is given, *as long
as it does not return it*. The following example shows a practical use-case for
this.
2018-10-26 11:29:53 +03:00
```
app = Sanic(__name__)
@app.middleware('request')
async def add_key(request):
# Add a key to request object like dict object
request['foo'] = 'bar'
@app.middleware('response')
async def custom_banner(request, response):
response.headers["Server"] = "Fake-Server"
@app.middleware('response')
async def prevent_xss(request, response):
response.headers["x-xss-protection"] = "1; mode=block"
app.run(host="0.0.0.0", port=8000)
```
The above code will apply the three middleware in order. The first middleware
2019-01-02 20:37:26 +08:00
**add_key** will add a new key `foo` into `request` object. This worked because
`request` object can be manipulated like `dict` object. Then, the second middleware
**custom_banner** will change the HTTP response header *Server* to
*Fake-Server*, and the last middleware **prevent_xss** will add the HTTP
header for preventing Cross-Site-Scripting (XSS) attacks. These two functions
are invoked *after* a user function returns a response.
## Responding early
If middleware returns a `HTTPResponse` object, the request will stop processing
and the response will be returned. If this occurs to a request before the
relevant user route handler is reached, the handler will never be called.
Returning a response will also prevent any further middleware from running.
2018-10-26 11:29:53 +03:00
```
@app.middleware('request')
async def halt_request(request):
return text('I halted the request')
@app.middleware('response')
async def halt_response(request, response):
return text('I halted the response')
```
2017-03-23 15:49:23 -04:00
## Listeners
If you want to execute startup/teardown code as your server starts or closes, you can use the following listeners:
- `before_server_start`
- `after_server_start`
- `before_server_stop`
- `after_server_stop`
2018-10-26 11:29:53 +03:00
These listeners are implemented as decorators on functions which accept the app object as well as the asyncio loop.
2017-03-23 15:49:23 -04:00
For example:
2018-10-26 11:29:53 +03:00
```
2017-03-23 15:49:23 -04:00
@app.listener('before_server_start')
async def setup_db(app, loop):
app.db = await db_setup()
@app.listener('after_server_start')
async def notify_server_started(app, loop):
print('Server successfully started!')
@app.listener('before_server_stop')
async def notify_server_stopping(app, loop):
print('Server shutting down!')
@app.listener('after_server_stop')
async def close_db(app, loop):
await app.db.close()
```
2018-10-26 11:29:53 +03:00
It's also possible to register a listener using the `register_listener` method.
2018-02-13 23:58:03 -08:00
This may be useful if you define your listeners in another module besides
the one you instantiate your app in.
2018-10-26 11:29:53 +03:00
```
2018-02-13 23:58:03 -08:00
app = Sanic()
2018-10-26 11:29:53 +03:00
2018-02-13 23:58:03 -08:00
async def setup_db(app, loop):
app.db = await db_setup()
2018-10-26 11:29:53 +03:00
2018-02-13 23:58:03 -08:00
app.register_listener(setup_db, 'before_server_start')
```
2017-03-23 15:49:23 -04:00
If you want to schedule a background task to run after the loop has started,
Sanic provides the `add_task` method to easily do so.
2018-10-26 11:29:53 +03:00
```
2017-03-23 15:49:23 -04:00
async def notify_server_started_after_five_seconds():
await asyncio.sleep(5)
print('Server successfully started!')
app.add_task(notify_server_started_after_five_seconds())
```
Sanic will attempt to automatically inject the app, passing it as an argument to the task:
2018-10-26 11:29:53 +03:00
```
async def notify_server_started_after_five_seconds(app):
await asyncio.sleep(5)
print(app.name)
app.add_task(notify_server_started_after_five_seconds)
```
Or you can pass the app explicitly for the same effect:
2018-10-26 11:29:53 +03:00
```
async def notify_server_started_after_five_seconds(app):
await asyncio.sleep(5)
print(app.name)
app.add_task(notify_server_started_after_five_seconds(app))
`