其它
1.隐式引入与显式引入
pait
支持多个Web框架, 如果项目对应的依赖环境中只安装了其中的一个框架, 那么可以直接使用隐式引入:
from pait.app.any import pait, load_app, add_simple_route
但是如果同时安装了多个框架, 那么隐式import将会引发异常, 建议使用显示引入,如下:
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
是每个CoreModel
的载体,Pait
装饰路由函数时会生成一个CoreModel
并存放在pait.g.data
中,以便为配置,文档生成等功能提供支持。
2.2.load_app
CoreModel
会存储很多路由函数的信息, 但是路由函数缺少关键的OpenAPI信息数据如url
, method
等,
所以在使用OpenAPI之前还需要使用load_app
补全数据,它的使用方法很简单,不过需要要在注册所有路由后再调用,如下:
2.3.HTTP异常
Pait
为每个Web框架提供了一个HTTP异常生成函数,它通过HTTP状态码,错误内容,Headers等参数生成Web框架的HTTP标准异常,它们的使用方法如下:
此外,Pait
还提供了一套常见的HTTP异常响应的Model,如下:
from pait.app.any 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
通过SimpleRoute统一了不同Web框架的路由注册以及生成响应的方法。
开发者通过SimpleRoute可以在不考虑兼容的情况下很方便的完成路由创建和注册。
Note
统一的路由响应生成功能由UnifiedResponsePluginProtocol
插件提供,
路由函数被注册时会为路由函数添加UnifiedResponsePluginProtocol
插件
SimpleRoute的使用方法如下:
docs_source_code/other/flask_with_simple_route_demo.py |
---|
| from flask import Flask
from pait.app.any 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 |
---|
| from starlette.applications import Starlette
from pait.app.any import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.starlette import pait
from pait.model import response
@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__":
import uvicorn
uvicorn.run(app)
|
docs_source_code/other/sanic_with_simple_route_demo.py |
---|
| from sanic import Sanic
from sanic.request import Request
from pait.app.any import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.sanic import pait
from pait.model import response
@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__":
import uvicorn
uvicorn.run(app)
|
docs_source_code/other/tornado_with_simple_route_demo.py |
---|
| from tornado.ioloop import IOLoop
from tornado.web import Application
from pait.app.any import SimpleRoute, add_multi_simple_route, add_simple_route
from pait.app.tornado import pait
from pait.model import response
@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
的基础类型,返回的Python
基础类型需要跟响应模型的response_data
保持一致。
第二段高亮是通过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 都应该不同 |
在运行代码后,通过curl
命令测试路由可以正常工作:
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.any 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
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from pait.app.any import get_app_attribute, set_app_attribute
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__":
import uvicorn
uvicorn.run(app)
|
docs_source_code/other/sanic_with_attribute_demo.py |
---|
| import httpx
from sanic import HTTPResponse, Request, Sanic, json
from pait.app.any import get_app_attribute, set_app_attribute
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__":
import uvicorn
uvicorn.run(app)
|
docs_source_code/other/tornado_with_attribute_demo.py |
---|
| import httpx
from tornado.ioloop import IOLoop
from tornado.web import Application, RequestHandler
from pait.app.any import get_app_attribute, set_app_attribute
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}
通过结果可以看到,路由函数能够获取到client
并通过client
获取到url的status_code
。
3.如何在其它Web框架使用Pait
目前Pait
还在快速迭代中,所以还是以功能开发为主,如果要在其他尚未支持的框架中使用Pait
, 或者要对功能进行拓展, 可以参照两个框架进行简单的适配即可.
同步类型的web框架请参照 pait.app.flask
异步类型的web框架请参照 pait.app.starlette
4.示例代码
更多完整示例请参考example
5.发行说明
详细的发版说明见CHANGELOG