# 中間件和監聽器
中間件是一類特定方法,它們會在服務器收到request之前或之后執行,通常用來定制request和response。此外,Sanic還提供了監聽器機制,可以在程序生命周期的不同點運行自己的代碼。
## 中間件
有兩種類型的中間件:request和response,都由`@app.middleware`裝飾器定義,定義時通過字符串參數`request`或`response`來設定類型。Response中間件需要接收request和response作為參數。中間件可以修改傳給它的request和response參數,同時不返回。
```python
app = Sanic(__name__)
@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)
```
上述代碼實現了兩個有序的中間件:
0. **custom_banner**會將HTTP頭`Server`鍵值改為`Fake-Server`
0. **prevent_xss**會將加一個HTTP頭,來防止跨域攻擊。
如果藍圖中也有中間件,可以看到中間件執行順序是:
**request** → **藍圖中request** → **藍圖視圖request** → **藍圖視圖response** → **藍圖response** → **response**
### 提前返回
如果藍圖中返回了`HTTPResponse`對象,后續request會停止執行,并返回response。如果這條語句是在用戶請求的路由handler前被調用的,那么handler將不會被調用。例如:
```python
@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')
```
## 監聽器
如果你想在服務啟動/結束執行初始化/清理程序,那么可以使用下述監聽器:
- before_server_start
- after_server_start
- before_server_stop
- after_server_stop
監聽器可以像普通裝飾器一樣使用,例如:
```python
@app.listener('before_server_start')
async def before_server_start(app, loop):
print('\n', before_server_start.__name__, '\n')
@app.listener('after_server_start')
async def after_server_start(app, loop):
print('\n', after_server_start.__name__, '\n')
@app.listener('before_server_stop')
async def before_server_stop(app, loop):
print('\n', before_server_stop.__name__, '\n')
@app.listener('after_server_stop')
async def after_serfer_stop(app, loop):
print('\n', after_serfer_stop.__name__, '\n')
```
執行順序是:
**before_server_start** → **after_server_start** → **before_server_stop** → **after_serfer_stop**
如果你想要在loop啟動之后,執行一些后臺任務,可以使用Sanic的`add_task`方法。
```python
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())
```