Skip to content

Introduction

Pait的核心是一个装饰器,这个装饰器只做被装饰函数的处理和插件的初始化,真正负责功能实现的都是这些被装饰器初始化的插件,其中上面所述Pait的类型转换与参数校验功能是Pait的一个核心插件。

简单介绍

开发者可以通过Pait传入需要的插件,然后程序在启动的时候,会以拦截器的形式把插件按照顺序进行初始化,如果该插件是前置形插件,那么它会被放置在类型转换与参数校验插件之前,否则就会放在后面。 前置插件与后置插件除了他们自身的is_pre_core属性不同外,它们的最主要的区别是获得到的参数不同,前置插件获得的是Web框架传递过来的请求参数,可以把它当成一个简单版的中间件,而后置形插件读到的是Pait核心插件转换后的请求数据,以下面的函数为例子:

import uvicorn  # type: ignore
from starlette.applications import Starlette
from starlette.responses import JSONResponse
from starlette.routing import Route

from pait.app.starlette import pait
from pait import field


@pait()
async def demo(
    uid: str = field.Query.i(),
    user_name: str = field.Query.i(),
) -> JSONResponse:
    return JSONResponse({"uid": uid, "user_name": user_name})


app = Starlette(routes=[Route("/api/demo", demo, methods=["GET"])])

uvicorn.run(app)
假设代码中的app已经装载了一个中间件和对应的Pait插件,在收到一个请求时,它的处理逻辑会变为如下图: pait-plugin

当请求进来后会先由Web框架的中间件处理,然后Web框架会执行查找路由的功能,当找不到路由时就会返回Not Found的响应给客户端,如果找到了对应的路由,就会把请求传入到到Pait的处理逻辑。在Pait的处理逻辑中请求会先被前置插件处理, 这时候前置插件只能得到框架对应的request参数(如果是flask框架,则没有),当前置插件处理完毕后就会把请求传入到核心插件进行参数提取和校验转换,经核心插件处理完后会把提取的参数传递给后置插件,交由后置插件进行处理, 最后才经由后置插件把参数交给真正的路由函数处理生成响应并一一返回。

如何使用

目前Pait提供plugin_listpost_plugin_list来供开发者传入前置插件和后置插件,如下:

from pait.plugin.required import RequiredPlugin

@pait(post_plugin_list=[RequiredPlugin.build(required_dict={"email": ["username"]})])
这段代码使用到的是一个名为RequiredPlugin的插件,这个插件属于后置形插件,所以是以post_plugin_list来传入插件。同时,由于插件是在收到请求的时候才会进行初始化,为了防止多个请求共享到相同的插件变量,Pait不支持直接初始化插件,而是使用build方法来使用插件(所以不为__init__方法提供准确的函数签名)。

如果考虑到插件的复用,推荐使用create_factory函数,该函数支持PEP-612,支持IDE提醒和类型检查,create_factory使用方法如下:

from pait.util import create_factory

# 首先传入插件的build方法,并把build需要的参数传进去
# 然后得到的是一个可调用的方法
required_plugin = create_factory(RequiredPlugin.build)(required_dict={"email": ["username"]})

# 直接调用create_factory的返回,这时候插件会注入到路由函数中并进行一些初始化,同时也不影响其它路由函数的使用
@pait(post_plugin_list=[required_plugin()])
def demo_1():
    pass

@pait(post_plugin_list=[required_plugin()])
def demo_2():
    pass

关闭预检查

Pait是一个装饰器,用来装饰路由函数,所以在程序启动的时候会直接运行,装填各种参数。不过在把插件装填到路由函数时会调用到插件的pre_check方法,对用户使用插件是否正确进行校验,比如下面的代码:

@pait()
def demo(
    uid: str = Body.i(default=None)
)
在启动的时候核心插件会校验到用户填写的default值并不属于str类型,所以会抛出错误。不过这类检查可能会影响到程序的启动时间,所以建议在测试环境下才通过pre_check进行检查,而在生产环境则关闭,而关闭的方法很简单,通过设置环境变量PAIT_IGNORE_PRE_CHECK为True即可关闭检查。

Back to top