首页 > 数据库 > 数据库开发技术 > fastapi框架系列-~小知识点-自定义中间件
近期由于被炒鱿鱼了,忙于其他事情,停止更文少了,非常抱歉!哈哈对于Fastapi框架来说似乎目前使用起来其实是没什么大问题,本来想着给大家分享点什么的,但是好像没什么值得可以写的~因为似乎挺简单的~刚好近期有人问起关于中间件一些使用,这里其实之前的文章也有讲过但是讲的不是非常的清晰。这里我再次更新一下。
在WEB开发过程中,通常需要一种机制来处理请求前和响应后的一些钩子函数,对于这一些函数通常我们可以称为中间件。这些中间件通常是一个轻量、低级别的、可拔插化的,它可以用于全局范围内处理对客户端的请求和及响应做进一步处理。如果你有Flask使用经验的话,FastApi中的中间件和Flask提供的钩子函数是类似的。
在FastApi中中间件它的本质其实也只是类,而这个中间件的类,它遵循ASGI的规范,所以只要符合ASGI的规范中间件的类,都可以在Fastapi中进行使用。它主要处理场景是在我们的路由函数收到请求和返回响应内容这一个过程中做一些我们需要做的事情,所有它有时候其实可以理解是所有路由函数中的钩子函数。基于中间件这种特性,所以我们可以总结中间件的适用场景有以下几点:
在starlette的中间件中有提供一个BaseHTTPMiddleware,我们可以通过继承BaseHTTPMiddleware来扩展定制我们的中间件。如下示例代码所示:
from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import FastAPI,Request
app = FastAPI()
# 基于BaseHTTPMiddleware的中间件实例,
class TimeCcalculateMiddleware(BaseHTTPMiddleware):
# dispatch 必须实现
async def dispatch(self, request:Request, call_next):
print('TimeCcalculateMiddleware-Start')
start_time = time.time()
responser = await call_next(request)
process_time = round(time.time() - start_time, 4)
# 返回接口响应时间
responser.headers["X-Process-Time"] = f"{process_time} (s)"
print('TimeCcalculateMiddleware-End')
return responser
# 基于BaseHTTPMiddleware的中间件实例,
class AuthMiddleware(BaseHTTPMiddleware):
# dispatch 必须实现
async def dispatch(self, request:Request, call_next):
print('AuthMiddleware-Start')
responser = await call_next(request)
print('AuthMiddleware-End')
return responser
app.add_middleware(TimeCcalculateMiddleware)
app.add_middleware(AuthMiddleware)
@app.get("/index")
async def index():
print('index-Start')
return {
'code':200
}
启动服务随意访问,此时留意观察控制台输出信息如下:
AuthMiddleware-Start
TimeCcalculateMiddleware-Start
index-Start
TimeCcalculateMiddleware-End
AuthMiddleware-End
·通过分析可以得到,FastApi框架中的中间件最先注册中间件会进入到中间件的内层,而最后注册的中间件则存在中间件中最外层,所以它的中间件也是遵循洋葱模型机制的。
在BaseHTTPMiddleware中,我们可以得到一些信息,首先ASGI需要遵循的规范有:
from starlette.types import ASGIApp, Receive, Scope, Send
所以所有的ASGI的中间件都需要遵循这个规范,所以自定义的中间件第一个参数必须是ASGIApp的实例对象。
通过分析BaseHTTPMiddleware源码,我们可以得到:
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
) -> Response:
raise NotImplementedError() # pragma: no cover
上面dispatch必须是协程函数,所以继承实现的时候需要注意也必须使用协程的函数, 甚至于__call__函数也是协程的函数:
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
·····
········
在中间件中,我们的Request其中是需要依赖于规范中的scope, receive来组成,所以才有了:
request = Request(scope, receive=receive)
的实现,但是其中Request还包含了其他的信息,其中比如:
Request 的 request.scope 属性是包含关联请求元数据的字典。
Request 的 request.receive 方法是接收请求体的函数。
甚至于我们的可以直接使用HTTPConnection来获取的连接,只是它包含的属性没有Request的多:
conn = HTTPConnection(scope=scope)
from fastapi import FastAPI, Request
app = FastAPI()
from starlette.responses import PlainTextResponse
from starlette.types import ASGIApp, Receive, Scope, Send
from starlette.requests import HTTPConnection
import typing
class WhileIpMiddleware:
def __init__(self, app: ASGIApp,
allow_ip: typing.Sequence[str] = (),
) -> None:
self.app = app
self.allow_ip = allow_ip or "*"
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] in ("http", "websocket") and scope["scheme"] in ("http", "ws"):
conn = HTTPConnection(scope=scope)
if self.allow_ip and conn.client.host not in self.allow_ip:
response = PlainTextResponse(content="不在IP白名单内", status_code=403)
await response(scope, receive, send)
return
await self.app(scope, receive, send)
else:
await self.app(scope, receive, send)
app.add_middleware(WhileIpMiddleware,allow_ip=['127.0.0.2'])
@app.get("/index")
async def index():
print('index-Start')
return {
'code': 200
}
代码其实相对简单,没什么可说的哈哈!仅供参考!
如果大家有什么想知道的知识点的话,也可以欢迎留言哦!有时间的话,我会给大家讲讲,虽然不一定是对滴,但是相互交流哦!哈哈
以上仅仅是个人结合自己的实际需求,做学习的实践笔记!如有笔误!欢迎批评指正!感谢各位大佬!
END
简书:https://www.jianshu.com/u/d6960089b087
掘金:https://juejin.cn/user/2963939079225608
公众号:微信搜【程序员小钟同学】
小钟同学 | 文 【欢迎一起学习交流】| QQ:308711822
原文作者:程序员小钟同学;如有侵权,请联系管理员删除。