使用attrs支持sanic的openapi/swagger
sanic-attrs的Python项目详细描述
SANIC和ATTS支持Swagger 2.0/OpenAPI
使用以下功能增强您的应用程序:
sanicsanic注意:这是sanic openapi实现的一个分支,来自@channelcat,我非常喜欢它,但它缺少我想要的一些功能(我还使用了第三方库(attrs
)作为建模输入/输出模型类的默认值。
超快介绍
给你的sanic api一个ui和openapi文档,一切都是免费的!
安装
注意:由于这个fork是我的必需品,我想实现的许多功能仍然不可用,因此这个库的状态是pre alpha
!另外,不要尝试examples文件夹,它还没有转换!我真丢脸……
pip install sanic-attrs
添加openapi和swagger ui:
fromsanic_attrsimportswagger_blueprint,openapi_blueprintapp.blueprint(openapi_blueprint)app.blueprint(swagger_blueprint)
现在,您将在url/swagger
处拥有一个swagger用户界面。你的路线将根据蓝图自动分类。这是默认用法,但可以看到更高级的用法。继续读下去!
注意:swagger_blueprint
非常棒,但有时您不希望它以任何原因(安全性等)打开,因此您只能在运行debug=true
时才能使用它。我就是这么用的:微笑:
输入
当然,由于sanic attrs
是基于attrs
的,而python的目标版本是3.5+,因此模型的大多数类型定义都将完全使用python类型,要么是全局类型,要么是来自typing
库。此外,还支持枚举
!:火花:
以下是支持的类型(到目前为止):
int
浮点数
str
bool
日期
日期时间
字节
键入.any
键入.collection
键入.dict
键入.iterable
键入.list
键入.mapping
键入。可选
键入.sequence
键入.set
键入.union
关于列表
和dict
的说明:请使用typing.list和
typing.dict
进行此操作。
用法
使用简单的修饰符记录路由
fromsanic_attrsimportdoc@app.get("/user/<user_id:int>")@doc.summary("Fetches a user by ID")@doc.produces(SomeOutputModel)asyncdefget_user(request,user_id):...@app.post("/user")@doc.summary("Creates a user")@doc.consumes(SomeInputModel,location="body")asyncdefcreate_user(request):...
为输入/输出建模
是的,在此版本中,您需要具有描述性:wink:
importtypingfromsanic_attrsimportdocclassCar(doc.Model):make:str=doc.field(description="Who made the car")model:str=doc.field(description="Type of car. This will vary by make")year:int=doc.field(description="4-digit year of the car",required=False)classGarage(doc.Model):spaces:int=doc.field(description="How many cars can fit in the garage")cars:typing.List[Car]=doc.field(description="All cars in the garage")@app.get("/garage")@doc.summary("Gets the whole garage")@doc.produces(Garage)asyncdefget_garage(request):returnjson({"spaces":2,"cars":[{"make":"Nissan","model":"370Z"}]})
高级用法
硅ncedoc.model
和doc.field
对于@attr.s
decorator和attr.ib
函数来说只是语法糖,您可以使用这些提供的类和方法来表示模型,或者在模型中使用vanillaattr
。下面是一个复杂的示例,它显示了一个混合模型:
fromenumimportEnum,IntEnumfromtypingimport(Any,Collection,Dict,Iterable,List,Mapping,Optional,Sequence,Set,Union)importattrfromsanic_attrsimportdocclassPlatformEnum(str,Enum):XBOX1="XBOX1"PLAYSTATION4="PLAYSTATION4"PC="PC"classLanguageEnum(IntEnum):ENGLISH=1JAPANESE=2SPANISH=3GERMAN=4PORTUGUESE=5classSomething(doc.Model):some_name:str=doc.field(description="Something name")@attr.sclassAnotherSomething:another_name:str=attr.ib(metadata={"description":"Another field"})classGame(doc.Model):name:str=doc.field(description="The name of the game")platform:PlatformEnum=doc.field(description="Which platform it runs on")score:float=doc.field(description="The average score of the game")resolution_tested:str=doc.field(description="The resolution which the game was tested")genre:List[str]=doc.field(description="One or more genres this game is part of")genre_extra:Sequence[str]=doc.field(description="One or more genres this game is part of")rating:Dict[str,float]=doc.field(description="Ratings given on each country")rating_outside:Mapping[str,float]=doc.field(description="Ratings given on each country")screenshots:Set[bytes]=doc.field(description="Screenshots of the game")screenshots_extra:Collection[bytes]=doc.field(description="Screenshots of the game")players:Iterable[str]=doc.field(description="Some of the notorious players of this game")review_link:Optional[str]=doc.field(description="The link of the game review (if exists)")junk:Union[str,bytes]=doc.field(description="This should be strange")more_junk:Any=doc.field(description="The more junk field")language:LanguageEnum=doc.field(description="The language of the game")something:List[Something]=doc.field(description="Something to go along the game")another:AnotherSomething=doc.field(description="Another something to go along the game")
关于输入提示或类型
参数的说明
您可能已经注意到,在上面的示例中,所有变量都是使用键入提示创建的。虽然这有点有趣,但您可能还想使用type
参数,这是attr
包提供的,而且sanic attrs
完全可以。因此,我们的游戏
类更希望如下所示:
classGame(doc.Model):name=doc.field(type=str,description="The name of the game")platform=doc.field(type=PlatformEnum,description="Which platform it runs on")score=doc.field(type=float,description="The average score of the game")resolution_tested=doc.field(type=str,description="The resolution which the game was tested")genre=doc.field(type=List[str],description="One or more genres this game is part of")genre_extra=doc.field(type=Sequence[str],description="One or more genres this game is part of")rating=doc.field(type=Dict[str,float],description="Ratings given on each country")rating_outside=doc.field(type=Mapping[str,float],description="Ratings given on each country")screenshots=doc.field(type=Set[bytes],description="Screenshots of the game")screenshots_extra=doc.field(type=Collection[bytes],description="Screenshots of the game")players=doc.field(type=Iterable[str],description="Some of the notorious players of this game")review_link=doc.field(type=Optional[str],description="The link of the game review (if exists)")junk=doc.field(type=Union[str,bytes],description="This should be strange")more_junk=doc.field(type=Any,description="The more junk field")language=doc.field(type=LanguageEnum,description="The language of the game")something=doc.field(type=List[Something],description="Something to go along the game")another=doc.field(type=AnotherSomething,description="Another something to go along the game")
关于attrs
的许多功能的说明
在attrs
中有很多特性在声明模型时非常方便,例如验证器、工厂等。对于这个版本,计划对验证器使用一些语法糖(因为大多数规则可以提供给doc.field
)。由于没有足够的时间对模型进行实际测试(到目前为止),因此现在不鼓励在声明模型时使用其他功能,如factories
(或在本项目的生命周期内,尚未决定):困惑:
动态输入模型解析
在sanic attrs中有一些惊喜。假设您已经声明了您的模型、端点,您还必须接受request.json
并将其加载为您的模型?这似乎不对…fortunally编写了一个小型中间件来处理这些情况:wink:
要启用动态输入模型解析,您只需将blueprint
添加到您的sanic应用程序中,并直接从请求中使用input-obj
关键字访问对象:
fromsanic_attrsimportparser_blueprint# ...app.blueprint(parser_blueprint)# ...@app.post("/game",strict_slashes=True)@doc.summary("Inserts the game data into the database")@doc.response("200","Game inserted successfuly",model=SuccessOutput)@doc.response("403","The user couldn't insert game to application",model=ErrorOutput)@doc.consumes(Game,location="body",content_type="application/json")@doc.produces(SuccessOutput)asyncdefinsert_game(request):my_object=request["input_obj"]assertisinstance(my_object,Game)# your logic here
注意:没有要处理(真正的)损坏数据的验证。如果在填充模型时发生异常,您将发现您的input_obj
关键字将是none
,以及另一个包含给定异常(如果有)的键input_exc
。如果要进一步自定义此行为,以便不必在每个请求中都检查none
,则可以在将parser\u blueprint
添加到app
实例之后添加自己的中间件,如下所示:
fromsanic.responseimportjsonfromsanic_attrsimportparser_blueprint# ...app.blueprint(parser_blueprint)# ...@app.middleware("request")asyncdefcheck_if_input_is_none(request):if"input_obj"inrequest:ifrequest["input_obj"]isNone:# error handling herereturnjson({"error":request["input_exc"].args[0]},500)
动态输出模型序列化
为了简单起见,还可以处理attrs
对象的直接返回,而不必创建字典,然后序列化或调用sanic.responses.json
,尽管这正是在幕后运行的:
fromsanic_attrsimportresponse# ...@app.get("/game",strict_slashes=True)@doc.summary("Gets the most played game in our database")@doc.response("200","Game data",model=Game)@doc.response("403","The user can't access this endpoint",model=ErrorOutput)@doc.produces(Game)asyncdefget_game(request):game=Game(name="Cities: Skylines",platform="PC",score=9.0,resolution_tested="1920x1080",genre=["Simulators","City Building"],rating={"IGN":8.5,"Gamespot":8.0,"Steam":4.5},players=["Flux","strictoaster"],language=1)returnresponse.model(game)# <--- the game instance, to be further serialized
注意:记住要创建可以将其所有值序列化为json的模型:+1:
配置其他所有内容
pip install sanic-attrs0
类型尚未可用
这些是当前版本中的键入
中没有的类型(带有一些注释,以便我可以记住以后要做什么(如果需要)):
抽象集
-是否像集合?anystr
-这主要类似于可选的[str]或只是str?异步上下文管理器
-我认为不是变量异步生成器
-我认为不是变量异步的
-我认为不是变量异步迭代器
-我认为不是变量可等待的
-我认为不是变量binaryio
-嗯,我不知道……可能是字节?byte string
-可以是字节,因为openapi是{"type":"string","format":"byte"}
ct_co
-我都不知道这是什么…可调用
-不是VARiabel可调用元
-不是变量链表
-不是变量(?)classvar
-通用…容器
-通用上下文管理器
-不是变量协同程序
-不是变量计数器
-不是变量defaultdict
-也许像dict?- 像列表一样?
frozenset
-a"仅查看列表"?生成器
-不是变量通用的
-不可能-或任何?可哈希
-哈希映射?io
-hmmm,来自docs:"textio和binaryio的通用基类",所以…项目视图
-什么是项目?它继承自抽象集…来自文档:"一个集合是一个有限的、可迭代的容器。"迭代器
-不是变量kt
-泛型
键视图
-dict"readonly"?映射视图
-dict"只读"?匹配
-一般(我认为)方法描述符类型
-不是变量MethodWrapperType
-不是变量
可变映射
-映射基类,docs:"泛型类型的抽象基类。"可变序列
-同上,但对于序列mutableset
-同上,但对于setnamedtuple
-这里该怎么办?namedtuple只是一个带有变量的对象,变量可以是任何东西
namedtuplemeta
-namedtuple的基类newtype
-不是变量/泛型?noreturn
-不是变量
模式
-通用可逆的
大小
-一般supportsabs
-不是变量
支持字节
-不是变量支持示例
-不是变量supportsfloat
-不是变量
支持t
-不是变量
supportsround
-不是变量
t
-通用
键入检查
-????t_co
-????t_contra
-????text
-如果创建了str对象,则返回该对象,所以我将使用str还是映射它?textio
-缓冲区,如字节…绘制地图?元组
-好吧…类似元组的列表还是类似元组的元组[int,str,float]?元组
-元组的基类类型
-泛型typevar
-泛型
键入meta
-泛型
如果有任何遗漏或需要,请填写一个问题或提供一个公关。公关是最受欢迎的:笑脸:
待办事项
- []属性处理
必需的
字段(在openapi对象
模式中) - []使用类型提示来记录函数的返回(作为输出模式/模型)
- []适当的测试
- []增加用例
- []了解我是否可以在不调用路由器的情况下获得请求模型
- []文档
许可证
麻省理工学院,同sanic openapi
推荐PyPI第三方库
键入
中没有的类型(带有一些注释,以便我可以记住以后要做什么(如果需要)):抽象集
-是否像集合?anystr
-这主要类似于可选的[str]或只是str?异步上下文管理器
-我认为不是变量异步生成器
-我认为不是变量异步的
-我认为不是变量异步迭代器
-我认为不是变量可等待的
-我认为不是变量binaryio
-嗯,我不知道……可能是字节?byte string
-可以是字节,因为openapi是{"type":"string","format":"byte"}
ct_co
-我都不知道这是什么…可调用
-不是VARiabel可调用元
-不是变量链表
-不是变量(?)classvar
-通用…容器
-通用上下文管理器
-不是变量协同程序
-不是变量计数器
-不是变量defaultdict
-也许像dict?frozenset
-a"仅查看列表"?生成器
-不是变量通用的
-不可能-或任何?可哈希
-哈希映射?io
-hmmm,来自docs:"textio和binaryio的通用基类",所以…项目视图
-什么是项目?它继承自抽象集…来自文档:"一个集合是一个有限的、可迭代的容器。"迭代器
-不是变量kt
-泛型
键视图
-dict"readonly"?映射视图
-dict"只读"?匹配
-一般(我认为)方法描述符类型
-不是变量MethodWrapperType
-不是变量
可变映射
-映射基类,docs:"泛型类型的抽象基类。"可变序列
-同上,但对于序列mutableset
-同上,但对于setnamedtuple
-这里该怎么办?namedtuple只是一个带有变量的对象,变量可以是任何东西
namedtuplemeta
-namedtuple的基类newtype
-不是变量/泛型?noreturn
-不是变量
模式
-通用可逆的
大小
-一般supportsabs
-不是变量
支持字节
-不是变量支持示例
-不是变量supportsfloat
-不是变量
支持t
-不是变量
supportsround
-不是变量
t
-通用
键入检查
-????t_co
-????t_contra
-????text
-如果创建了str对象,则返回该对象,所以我将使用str还是映射它?textio
-缓冲区,如字节…绘制地图?元组
-好吧…类似元组的列表还是类似元组的元组[int,str,float]?元组
-元组的基类类型
-泛型typevar
-泛型
键入meta
-泛型
必需的
字段(在openapi对象
模式中)sanic openapi