中间件和侦听器

中间件是在请求服务器之前或之后执行的功能。它们可以用来修改 请求到答复 用户定义的处理函数。

此外,SANIC还提供了监听器,允许您在应用程序生命周期的不同点上运行代码。

中间件

中间件有两种类型:请求和响应。两者都使用 @app.middleware decorator,decorator的参数是表示其类型的字符串: 'request''response' .

  • 请求中间件只接收 request 作为参数,并按它们的添加顺序执行。

  • 响应中间件接收 requestresponse 并在 颠倒 秩序。

最简单的中间件根本不会修改请求或响应:

@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)

三个中间件按以下顺序执行:

  1. 第一个请求中间件 add_key 添加新密钥 foo 进入请求上下文。

  2. 请求被路由到处理程序 指数 ,它从上下文获取密钥并返回文本响应。

  3. 第二响应中间件 prevent_xss 添加用于防止跨站点脚本(XSS)攻击的HTTP头。

  4. 第一个响应中间件 custom_banner 更改HTTP响应头 服务器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 . 一个典型的用例是将从数据库获取的用户对象存储在身份验证中间件中。添加的密钥在请求期间可供所有后续中间件以及处理程序访问。

自定义上下文是为应用程序和扩展保留的。萨尼奇自己没有利用它。

听众

如果要在服务器启动或关闭时执行启动/拆卸代码,可以使用以下侦听器:

  • before_server_start

  • after_server_start

  • before_server_stop

  • after_server_stop

这些侦听器在接受应用程序对象和Asyncio循环的函数上作为修饰符实现。

例如:

@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“u start handler”中的第一个侦听器设置了数据库连接,则在它之后注册的侦听器可以依赖于该连接在启动和停止时都处于活动状态,因为停止是按相反的顺序进行的,而数据库连接是最后断开的。

也可以使用 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))