其它
1.隐式引入与显式引入
pait
支持多个Web框架, 如果项目对应的依赖环境中只安装了其中的一个框架, 那么可以直接使用隐式引入:
from pait.app import pait, load_app, add_simple_route
但是如果同时安装了多个框架, 那么上面的导包方式将会引发异常, 建议使用显示引入,如下:
from pait.app.flask import pait, load_app, add_simple_route
from pait.app.sanic import pait, load_app, add_simple_route
from pait.app.starlette import pait, load_app, add_simple_route
from pait.app.tornado import pait, load_app, add_simple_route
2.内部方法
Pait
内部封装了一些通用的方法,通过这些方法,开发者可以在不考虑兼容不同的Web框架的情况下快速的开发出拓展包,或者对Pait
进行拓展,
OpenAPI路由和grpc-gateway就是基于这些方法开发的。
2.1.data
data
是Pait
的数据载体,Pait
在装饰路由函数时生成的数据会按照一定的规则存放在pait.g.data
中,以便为后续的配置,文档生成等功能提供支持。
2.2.load_app
data会存储很多路由函数的信息, 但是会缺少关键的OpenAPI信息数据如url
, method
等,
所以在使用OpenAPI之前还需要使用load_app
把相关参数与pait
装饰器装饰的路由函数数据在data中绑定,使用方法很简单,不过它一定要在注册所有路由后再调用,如下:
Note
OpenAPI模块在初始化之前也会通过load_app
方法加载数据
2.3.HTTP错误异常
Pait
为每个Web框架封装了一个HTTP异常生成函数,它们通过HTTP状态码,错误内容,Headers等参数生成Web框架的HTTP标准异常,常用于身份校验等模块,它们的使用方法如下:
此外,Pait
还提供了一套常见的HTTP异常响应的Model,如下:
from pait.app import pait
from pait.model import response
# response.Http400RespModel
# response.Http401RespModel
# response.Http403RespModel
# response.Http404RespModel
# response.Http405RespModel
# response.Http406RespModel
# response.Http407RespModel
# response.Http408RespModel
# response.Http429RespModel
@pait(response_model_list=[response.Http400RespModel])
def demo() -> None:
pass
同时HTTP异常响应的Model也支持自定义创建,如下使用示例:
from pait.model import response
# 创建一个状态码为500,content-type为html的响应Model
response.HttpStatusCodeBaseModel.clone(resp_model=response.HtmlResponseModel, status_code=500)
# 创建一个状态码为500,content-type为text的响应Model
response.HttpStatusCodeBaseModel.clone(resp_model=response.TextResponseModel, status_code=500)
2.4.SimpleRoute
Pait
除了统一了不同Web框架的请求处理外,还通过SimpleRoute统一了不同Web框架的路由注册方法以及路由生成响应的方法。
开发者通过SimpleRoute可以在不考虑兼容的情况下很方便的完成路由编写和注册功能,
比如grpc-gateway和OpenAPI路由功能完全使用SimpleRoute的方式编写路由,并由SimpleRoute注册到对应的Web框架中,节省了很多工作量。
Note
统一的路由生成响应功能由UnifiedResponsePluginProtocol
插件提供,SimpleRoute在注册路由函数时会为路由函数使用UnifiedResponsePluginProtocol
插件
SimpleRoute的使用方法如下:
docs_source_code/other/flask_with_simple_route_demo.py |
---|
| from flask import Flask
from pait.app import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.flask import pait
from pait.model import response
@pait(response_model_list=[response.JsonResponseModel])
def json_route() -> dict:
return {}
@pait(response_model_list=[response.TextResponseModel])
def text_route() -> str:
return "demo"
@pait(response_model_list=[response.HtmlResponseModel])
def html_route() -> str:
return "<h1>demo</h1>"
app: Flask = Flask("demo")
add_simple_route(app, SimpleRoute(route=json_route, url="/json", methods=["GET"]))
add_multi_simple_route(
app,
SimpleRoute(route=json_route, url="/json", methods=["GET"]),
SimpleRoute(route=text_route, url="/text", methods=["GET"]),
SimpleRoute(route=html_route, url="/html", methods=["GET"]),
prefix="/api",
title="api",
)
if __name__ == "__main__":
app.run(port=8000)
|
docs_source_code/other/starlette_with_simple_route_demo.py |
---|
| import uvicorn
from pait.app import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.starlette import pait
from pait.model import response
from starlette.applications import Starlette
@pait(response_model_list=[response.JsonResponseModel])
async def json_route() -> dict:
return {}
@pait(response_model_list=[response.TextResponseModel])
async def text_route() -> str:
return "demo"
@pait(response_model_list=[response.HtmlResponseModel])
async def html_route() -> str:
return "<h1>demo</h1>"
app: Starlette = Starlette()
add_simple_route(app, SimpleRoute(route=json_route, url="/json", methods=["GET"]))
add_multi_simple_route(
app,
SimpleRoute(route=json_route, url="/json", methods=["GET"]),
SimpleRoute(route=text_route, url="/text", methods=["GET"]),
SimpleRoute(route=html_route, url="/html", methods=["GET"]),
prefix="/api",
title="api",
)
if __name__ == "__main__":
uvicorn.run(app)
|
docs_source_code/other/sanic_with_simple_route_demo.py |
---|
| import uvicorn
from pait.app import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.sanic import pait
from pait.model import response
from sanic import Sanic
from sanic.request import Request
@pait(response_model_list=[response.JsonResponseModel])
async def json_route(request: Request) -> dict:
return {}
@pait(response_model_list=[response.TextResponseModel])
async def text_route(request: Request) -> str:
return "demo"
@pait(response_model_list=[response.HtmlResponseModel])
async def html_route(request: Request) -> str:
return "<h1>demo</h1>"
app: Sanic = Sanic("demo")
add_simple_route(app, SimpleRoute(route=json_route, url="/json", methods=["GET"]))
add_multi_simple_route(
app,
SimpleRoute(route=json_route, url="/json", methods=["GET"]),
SimpleRoute(route=text_route, url="/text", methods=["GET"]),
SimpleRoute(route=html_route, url="/html", methods=["GET"]),
prefix="/api",
title="api",
)
if __name__ == "__main__":
uvicorn.run(app)
|
docs_source_code/other/tornado_with_simple_route_demo.py |
---|
| from pait.app import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.tornado import pait
from pait.model import response
from tornado.ioloop import IOLoop
from tornado.web import Application
@pait(response_model_list=[response.JsonResponseModel])
async def json_route() -> dict:
return {}
@pait(response_model_list=[response.TextResponseModel])
async def text_route() -> str:
return "demo"
@pait(response_model_list=[response.HtmlResponseModel])
async def html_route() -> str:
return "<h1>demo</h1>"
app: Application = Application()
add_simple_route(app, SimpleRoute(route=json_route, url="/json", methods=["GET"]))
add_multi_simple_route(
app,
SimpleRoute(route=json_route, url="/json", methods=["GET"]),
SimpleRoute(route=text_route, url="/text", methods=["GET"]),
SimpleRoute(route=html_route, url="/html", methods=["GET"]),
prefix="/api",
title="api",
)
if __name__ == "__main__":
app.listen(8000)
IOLoop.instance().start()
|
代码中第一段高亮代码是编写了三个路由函数,它们都符合SimpleRoute的路由函数标准,SimpleRoute路由函数的标准如下:
- 1.路由函数需要被
pait
装饰,同时response_model_list属性不能为空(代码中路由函数的响应模型分别为JsonResponseModel
,TextResponseModel
,HtmlResponseModel
,这些都是SimpleRoute路由强制要求的,如果没有响应模型,那么无法通过SimpleRoute把路由函数注册到Web框架中。)
- 2.路由函数的返回值不再是各种响应对象,而是
Python
的基础类型,但是需要跟响应模型保持一致。
第二段高亮是通过add_simple_route
和add_multi_simple_route
方法注册路由,其中add_simple_route
只能注册一个路由,而add_multi_simple_route
可以注册多个路由,它们的都接收app和SimpleRoute实例,而SimpleRoute只支持三个属性,如下:
参数 |
描述 |
route |
符合SimpleRoute标准的路由函数 |
url |
当前路由的Url |
method |
当前路由对应的HTTP Method |
此外,add_multi_simple_route
还支持两个可选参数,如下:
参数 |
描述 |
prefix |
路由前缀,比如prefix为"/api",某个SimpleRoute的url为"/user"时,注册的路由URL为"/api/user" |
title |
当前路由组的标题是什么,对于某些框架,它们采用的路由组或者蓝图都需要有唯一的命名,所以不同批次add_multi_simple_route 的title 参数都应该不同 |
add_simple_route
和add_multi_simple_route
在添加路由函数时,会先检查路由函数是否符合SimpleRoute标准,如果不符合,则抛出异常,
如果符合,会使用UnifiedResponsePluginProtocol
插件使路由函数的返回转换为符合Web框架的响应类型,最后再把路由函数注册到Web框架中。
在运行代码后,可以通过以下命令进行测试:
curl http://127.0.0.1:8000/json{} curl http://127.0.0.1:8000/api/json{} curl http://127.0.0.1:8000/api/textdemo curl http://127.0.0.1:8000/api/html<h1>demo</h1>
2.5.设置与获取Web框架属性
Pait
为Web框架的设置与获取Web框架属性值的方法提供了一个统一的方法,它们分别是set_app_attribute
和get_app_attribute
,
通过set_app_attribute
和get_app_attribute
可以在任一时刻设置与获取Web框架属性,使用方法如下:
docs_source_code/other/flask_with_attribute_demo.py |
---|
| import httpx
from flask import Flask, Response, current_app, jsonify
from pait.app import get_app_attribute, set_app_attribute
def demo_route() -> Response:
client: httpx.Client = get_app_attribute(current_app, "client")
return jsonify({"status_code": client.get("http://so1n.me").status_code})
app: Flask = Flask("demo")
app.add_url_rule("/api/demo", "demo", demo_route, methods=["GET"])
set_app_attribute(app, "client", httpx.Client())
if __name__ == "__main__":
app.run(port=8000)
|
docs_source_code/other/starlette_with_attribute_demo.py |
---|
| import httpx
import uvicorn
from pait.app import get_app_attribute, set_app_attribute
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
async def demo_route(request: Request) -> JSONResponse:
client: httpx.AsyncClient = get_app_attribute(request.app, "client")
return JSONResponse({"status_code": (await client.get("http://so1n.me")).status_code})
app: Starlette = Starlette()
app.add_route("/api/demo", demo_route, methods=["GET"])
set_app_attribute(app, "client", httpx.AsyncClient())
if __name__ == "__main__":
uvicorn.run(app)
|
docs_source_code/other/sanic_with_attribute_demo.py |
---|
| import httpx
import uvicorn
from pait.app import get_app_attribute, set_app_attribute
from sanic import HTTPResponse, Request, Sanic, json
async def demo_route(request: Request) -> HTTPResponse:
client: httpx.AsyncClient = get_app_attribute(request.app, "client")
return json({"status_code": (await client.get("http://so1n.me")).status_code})
app: Sanic = Sanic("demo")
app.add_route(demo_route, "/api/demo", methods=["GET"])
set_app_attribute(app, "client", httpx.AsyncClient())
if __name__ == "__main__":
uvicorn.run(app)
|
docs_source_code/other/tornado_with_attribute_demo.py |
---|
| import httpx
from pait.app import get_app_attribute, set_app_attribute
from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler
class DemoHandler(RequestHandler):
async def get(self) -> None:
client: httpx.AsyncClient = get_app_attribute(self.application, "client")
self.write({"status_code": (await client.get("http://so1n.me")).status_code})
app: Application = Application()
app.add_handlers(".*$", [("/api/demo", DemoHandler)])
set_app_attribute(app, "client", httpx.AsyncClient())
if __name__ == "__main__":
app.listen(8000)
IOLoop.instance().start()
|
在运行代码后,可以通过以下命令进行测试:
➜ curl http://127.0.0.1:8000/api/demo
{"status_code": 200}
3.如何在其它Web框架使用Pait
目前Pait
还在快速迭代中,所以还是以功能开发为主,如果要在其他尚未支持的框架中使用Pait
, 或者要对功能进行拓展, 可以参照两个框架进行简单的适配即可.
同步类型的web框架请参照 pait.app.flask
异步类型的web框架请参照 pait.app.starlette
4.IDE支持
pait的类型校验和转换以及类型拓展得益于Pydantic
,同时也从pydantic
获得到IDE的支持,目前支持Pycharm
和Mypy
5.示例代码
更多完整示例请参考example
6.发行说明
详细的发版说明见CHANGELOG