ITPub博客

首页 > 数据库 > 数据库开发技术 > fastapi框架系列-~小知识点-自定义中间件

fastapi框架系列-~小知识点-自定义中间件

原创 数据库开发技术 作者:程序员小钟同学 时间:2022-03-18 22:33:21 0 删除 编辑

近期由于被炒鱿鱼了,忙于其他事情,停止更文少了,非常抱歉!哈哈对于Fastapi框架来说似乎目前使用起来其实是没什么大问题,本来想着给大家分享点什么的,但是好像没什么值得可以写的~因为似乎挺简单的~刚好近期有人问起关于中间件一些使用,这里其实之前的文章也有讲过但是讲的不是非常的清晰。这里我再次更新一下。

Fastapi框架中的中间件

在WEB开发过程中,通常需要一种机制来处理请求前和响应后的一些钩子函数,对于这一些函数通常我们可以称为中间件。这些中间件通常是一个轻量、低级别的、可拔插化的,它可以用于全局范围内处理对客户端的请求和及响应做进一步处理。如果你有Flask使用经验的话,FastApi中的中间件和Flask提供的钩子函数是类似的。

Fastapi框架中间件使用场景

在FastApi中中间件它的本质其实也只是类,而这个中间件的类,它遵循ASGI的规范,所以只要符合ASGI的规范中间件的类,都可以在Fastapi中进行使用。它主要处理场景是在我们的路由函数收到请求和返回响应内容这一个过程中做一些我们需要做的事情,所有它有时候其实可以理解是所有路由函数中的钩子函数。基于中间件这种特性,所以我们可以总结中间件的适用场景有以下几点:

  • ● 请求跨域处理
  • ● API接口限流
  • ● 日志请求记录
  • ● IP白名单限制处理
  • ● 请求权限的校验
  • ● 请求缓存处理

中间件自定义

基于BaseHTTPMiddleware自定义

在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需要遵循的规范

在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

在中间件中,我们的Request其中是需要依赖于规范中的scope, receive来组成,所以才有了:

  request = Request(scope, receive=receive)

的实现,但是其中Request还包含了其他的信息,其中比如:

  • Request 的 request.scope 属性是包含关联请求元数据的字典。

  • Request 的 request.receive 方法是接收请求体的函数。

甚至于我们的可以直接使用HTTPConnection来获取的连接,只是它包含的属性没有Request的多:

   conn = HTTPConnection(scope=scope)

基于类自定的实现一个IP白名单中间件:

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


原文作者:程序员小钟同学;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2021-02-20

  • 博文量
    224
  • 访问量
    111693