中间件和监听器
中间件是在对服务器的请求之前或之后执行的功能。它们可用于修改对用户定义的处理函数的请求或响应。
此外,Sanic还提供了侦听器,使您可以在应用程序生命周期的各个阶段运行代码。
中间件
中间件有两种类型:请求和响应。两者都使用@app.middleware
装饰器声明,装饰器的参数是一个表示其类型的字符串:'request'
或'response'
。
- 请求中间件仅接收请求作为参数。
- 响应中间件同时接收请求和响应。
最简单的中间件根本不会修改请求或响应:
@app.middleware('request')
async def print_on_request(request):
print("I print when a request is received by the server")
@app.middleware('response')
async def print_on_response(request, response):
print("I print when a response is returned by the server")
修改请求或响应
中间件可以修改给定的请求或响应参数,只要它不返回即可。以下示例显示了一个实际的用例。
app = Sanic(__name__)
@app.middleware('request')
async def add_key(request):
# Arbitrary data may be stored in request context:
request.ctx.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.get("/")
async def index(request):
return sanic.response.text(request.ctx.foo)
app.run(host="0.0.0.0", port=8000)
三个中间件按顺序执行:
- 第一个请求中间件
add_key
将新的主键foo
添加到请求上下文中。 - 请求被路由到控制器
index
,该控制器从上下文获取主键foo
并返回文本响应。 - 第二个响应中间件
prevent_xss
添加了HTTP标头,以防止跨站点脚本(XSS)攻击。 - 第一个响应中间件
custom_banner
将HTTP响应标头Server更改为Fake-Server
提前响应
如果中间件返回HTTPResponse
对象,则该请求将停止处理并返回响应。如果在到达相关的用户路由处理程序之前在请求中发生这种情况,则永远不会调用该处理程序。返回响应也将阻止任何其他中间件运行。
@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')
自定义上下文
任意数据可以存储在request.ctx
中。典型的用例是将从数据库中获取的用户对象存储在身份验证中间件中。在请求期间,所有以后的中间件以及处理程序都可以访问添加的密钥。
自定义上下文保留给应用程序和扩展。Sanic本身不使用它。
监听器
如果要在服务器启动或关闭时执行启动/拆卸代码,则可以使用以下侦听器:
before_server_start
after_server_start
before_server_stop
after_server_stop
这些侦听器在接受应用程序对象以及异步循环的函数上作为装饰器实现。
@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()
注意:监听者被解构的顺序与创建的顺序相反。
如果before_server_start
中的第一个侦听器设置了数据库连接,那么在它之后注册的侦听器可以依赖于该连接在启动和停止时都处于活动状态,因为停止是按相反的顺序进行的,而数据库连接是在最后被拆掉的。
也可以使用register_listener
方法注册一个侦听器。如果您在实例化应用程序的模块之外的其他模块中定义了侦听器,这可能会很有用。
app = Sanic(__name__)
async def setup_db(app, loop):
app.db = await db_setup()
app.register_listener(setup_db, 'before_server_start')
如果您希望安排后台任务在循环开始后运行,那么Sanic提供了add_task
方法可以轻松地做到这一点。
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将尝试自动注入应用程序,并将其作为参数传递给任务:
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)
或者,您可以显式地传递应用程序以达到相同的效果:
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))