最近在使用apiflask框架,总体来说这个框架十分的赞!
在使用的过程中遇到了一下的一些问题或者说是疑问,有的问题已经自行解决但不知道是否有更好的方法。
因此发个贴看看有没有更好的、更优雅的解决方法。
(表达能力不太好,见谅 and 由于写作的时间跨度比较大,因此个别方法已在官方文档中贴出,但下文并未提及,有的也可能是我眼瞎没看到)
自定义通用响应体
个人感觉这个问题是比较重要的,目前的解决方法过于丑陋。
当前的解决办法(Flask通用),是先定义一个格式化响应体的函数,然后将内容格式化成字典然后返回,但这样就不知道应该如何与@output
装饰器配合使用了,感觉这个问题应该是有成熟的解决方案才对。
其中这个自定义的响应体应该是全局通用的,包括错误处理之类(框架自动处理 or 开发者手动处理的错误)
def make_res(code:int=200, message:str="ok", data: Any=None) -> dict:
return {
"code": code,
"message": message,
"data": data,
"timestamp": get_timestamp()
}
@app.get('/')
def index():
...
return make_res( ... )
Abort函数
abort
函数应该如何搭配上面的自定义响应体 一起使用?
有的时候错误会有状态码需要返回出去,但abort
好像只可以输入http协议范围内的错误状态码(好像是因为会和http的status code绑定在一起)
@docs装饰器的使用
apiflask
直接生成api文档这个功能十分的cool~,但随之而来的问题是我应该如何指定文档中参数的类型,比如form
表单中文件上传。
api文档中的Example Value
应该如何修改
Example Value
目前的解决方法是在Schema
模型中指定missing
和default
字段
@input装饰器的使用
官网中给出了input可以location到files
、‘form’、‘cookies’、‘headers’和’query’
其中files
不太清楚如何获取.
关于用户认证
用户认证是极其重要的一块,一般前后端交互中使用token
实现。查看apiflask
官网发现到目前位置仍然没有找到相关部分的栗子。所幸apiflask使用了flask-httpauth
模块蹭使用过,一下是我的解决方案,同样的个人对这个实现方法不太满意,感觉比较丑陋。代码如下:
问题:
当使用类的方法(MethodView)来组织时,如何添加认证
如何为整个类添加认证 → 未知
如何为类中的个别方法添加 → 为这个类的方法添加装饰器@auth_required
当使用普通视图函数时应该如何添加 → 直接添加@auth_required
extend.py
from apiflask import HTTPTokenAuth
@auth.verify_token
def verify_token(token):
"""验证token
验证函数,验证token是否有效
Args:
token (str): 前端传来的token
Returns:
Bool: True表示token验证有效 否则False
"""
g.user = None
try:
data = Serializer(current_app.config['SECRET_KEY']).loads(token)
except Exception as e:
return False
if "username" in data:
g.user = data["username"]
return True
return False
utils.py
def generate_auth_token(payload:dict, expiration=3600 * 24 * 7):
"""生成token
Args:
payload (dict): token载体
expiration (int, optional): token有效时间. Defaults to 3600*24*7.
Returns:
str: token令牌
"""
s = Serializer(current_app.config["SECRET_KEY"], expires_in=expiration)
return s.dumps(payload).decode()
views.py
from apiflask.decorators import auth_required
...
@bp.get("/")
@auth_required(auth)
def index():
user = {"msg": "not found"}
for item in users:
if item["username"] == g.user:
user = item
return user
ps: 上面的代码均已分类push到GitHub,新手上路 小心高血压!
1 个赞
z-t-y
(Andy Zhou)
2021 年8 月 5 日 03:32
2
2 个赞
Thx!之前被这个问题折磨了很久,最终无奈选择用Flask的方法来解决。
现在发现原来是需要在schema 的Raw
中指定type为file ,看了你的代码瞬间明白了
我精简了一下,方便有需要的直接上手
from marshmallow.fields import Raw
...
@app.post("/icon")
@input({"icon": Raw(type="file", required=True)}, location="files")
@doc(description="上传头像")
def icon(data):
print(data.get("icon"))
return {"msg": 'ok'}
除此之外,我还发现一个apispec 的bug – 无法上传文件。
在测试上述代码的时候我通过apiflask内置的apispec进行调试,发现后端无法获取到文件,转而使用postman。由此发现的这个bug(apispec无法上传,postman正常)
1 个赞
greyli
(Grey Li)
2021 年8 月 5 日 13:58
4
哇,终于收到一份详细反馈,感谢!我周末再来写详细回复。
感谢李大!贴中的问题由于时间问题,还有许多问题项目起来,希望可以开个贴(或者直接在本贴)以便及时反馈(一点小建议)
此外,这两天想起来不少问题(具体开发问题,非框架问题)希望新书能多介绍相关方面,内容如下:
异步任务
获取异步任务的内容(共享线程内容?)
消息队列
具体情景:
如写一个端口扫描器 ,由于扫描65535个端口耗时往往需要几分钟。此时接口应该向前端返回一个任务号(如celery的task id) 前端定时(或使用ws)获取任务进程、内容直到任务完成。
1 个赞
greyli
(Grey Li)
2021 年8 月 11 日 03:52
6
自定义通用响应体
如何自定义通用响应体并和 output 装饰器搭配使用
在 0.9.0 版本之后,你可以通过配置 BASE_RESPONSE_SCHEMA
来指定通用响应的模式:
from apiflask import APIFlask, Schema
from apiflask.fields import String, Integer, Field
app = APIFlask(__name__)
class BaseResponseSchema(Schema):
message = String()
code = Integer()
data = Field()
app.config['BASE_RESPONSE_SCHEMA'] = BaseResponseSchema
数据键默认是 data,你可以通过配置 BASE_RESPONSE_DATA_KEY
来自定义。
现在只需要在视图函数返回对应通用响应的字典即可,output 装饰器指定数据模式。APIFlask 自动序列化基础响应模式,以及嵌套的数据模式,并在 OpenAPI 中生成对应的 Schema:
@app.get('/pets')
@output(PetOutSchema(many=True))
def get_pets():
return make_resp('Success!', 200, pets)
更多介绍参考示例和文档:
Abort 函数
abort 函数目前还不支持传递额外的参数,后续会支持(详见 https://github.com/greyli/apiflask/issues/125 )。
@docs装饰器的使用
我应该如何指定文档中参数的类型,比如 form
表单中文件上传。
参数类型都通过数据的 input/output schema 的字段类型来指定。
api文档中的 Example Value
应该如何修改
整个数据模型的示例在 input 和 output 装饰器的 example 和 example 参数来给出。
单个字段的示例在字段定义中给出:
class MySchema(Schema):
name = String(metadata={'example': 'Mike'})
文档:https://apiflask.com/openapi/#response-and-request-example
@input 文件上传
官网中给出了input可以location到 files
、‘form’、‘cookies’、‘headers’和’query’
其中 files
不太清楚如何获取.
暂不支持上传类型的 OpenAPI 生成,后续会支持,详见 https://github.com/greyli/apiflask/issues/123 。考添加一个 File 字段来简化字段定义。
关于用户认证
当使用类的方法(MethodView)来组织时,如何为整个类添加认证
@app.route('/pets/<int:pet_id>', endpoint='pet')
class Pet(MethodView):
decorators = [auth_required(auth)] # <--
@output(PetOutSchema)
@doc(summary='Get a Pet')
def get(self, pet_id):
# ...
@auth_required(auth)
@input(PetInSchema)
@output(PetOutSchema)
def put(self, pet_id, data):
# ...
@input(PetInSchema(partial=True))
@output(PetOutSchema)
def patch(self, pet_id, data):
# ...
1 个赞
greyli
(Grey Li)
2021 年8 月 11 日 03:55
7
不客气~后续有问题写在这里就可以。
希望新书能多介绍相关方面,内容如下……
嗯,新书会考虑写这些,不过也许会放到那本 Flask 书的第二版,考虑到这些和 Web API 开发关系不大。
greyli
(Grey Li)
2021 年9 月 9 日 15:29
8
abort 函数加了一个 extra_data 参数来设置额外的字段(#130 ):
@app.get('/')
def missing():
abort(404, message='nothing', extra_data={'code': '123', 'status': 'not_found'})
你觉得怎么样?有没有什么改进想法?
1 个赞
关于Abort函数配合 Base response schema customization
问题描述:我设置了通用响应体,但这个通用响应体貌似不能配合abort函数使用( abort函数返回的响应体和通用响应体不一致)
下面是我相关的代码片段:
响应体设置
class Normal(Schema):
status_code = Integer(metadata={'example': 200})
message = String(metadata={'example': 'response message'})
data = Field()
视图函数
@input(...)
def put(self, item):
...
abort(400, message="出了点小问题")
当前的响应体
{
"detail": {},
"message": "出了点小问题"
}
我期望的响应
{
"data": null,
"message": "出了点小问题",
"status_code": 400
}
后话: 我正准备使用apiflask
来做一个简单的 todo list 项目,希望能在简单易懂的基础上涉及到该框架的大部分内容,欢迎各位大佬的指导~
因此上述代码也在该仓库中:
greyli
(Grey Li)
2021 年9 月 24 日 13:00
10
在错误响应里,APIFlask 只会自动生成两个字段 detail 和 message,其他字段由用户自己控制。所以你可以手动返回其他数据:
@input(...)
def put(self, item):
...
abort(400, message="出了点小问题", extra_data={"data": None, "status_code": 400})
或是注册一个错误处理函数:
@app.error_processor
def make_error_response(error):
return {
'detail': error.detail,
'status_code': error.status_code,
'message': error.message,
'data': {}
}
1 个赞
使用DateTime优雅的格式化时间
问题来源:微信群一群友想生成'%a, %d %b %Y %H:%M:%S GMT'
格式的时间
解决办法:
from apiflask.fields import DateTime
...
class OutSchema(Schema):
time = DateTime('%a, %d %b %Y %H:%M:%S GMT')
# DateTime(<自定义格式字符串>)
补充: DateTime
其实内置了两种标准化的格式–rfc(for RFC822)
和iso(for ISO8601)
,而默认是使用iso
标准进行格式化的,因此可以这样来生成rfc
标准的时间
class OutSchema(Schema):
time = DateTime('rfc')
# DateTime('rfc') or DateTime('rfc822') or DateTime('iso') or DateTime('iso8601')
apiflask官网文档描述问题
link:
Basic Usage - APIFlask
在官网如图所示描述,
data_key
属性貌似是指向ORM模型,
phone_number
字段是输出给前端的名字。
但实际上data_key
貌似才是输出给前端的名字,而schema
模型中属性名应该与ORM模型中的属性名一致。
相关验证代码:
orm模型的第19行:https://github.com/Farmer-chong/todo-list/blob/master/app/models.py
验证模型的第26行:https://github.com/Farmer-chong/todo-list/blob/master/app/schemas/output.py
commit id:fd44fae6375cf7b1485582faf71efedaebbb57fc
github discuss #147
wugeyi
(伍个一)
2021 年11 月 22 日 02:17
13
看到你这边有关于全局响应的问题,我这边有一种实现方式,可以参考下https://www.yuque.com/docs/share/411feb9c-1a2e-40d6-9450-3455e56dc836?# 《4. 全局响应/状态码》
1 个赞
通用响应体相关
自定义通用响应体在apiflas
中有更优雅的方式实现,当然文中的相关内容也很适合flask
,如果apiflask
可以进行适度的定制,然后配合错误响应就好了. @greyli
如该文中的重写 ok
、err
等方法
文件上传相关
Farmer-Chillax:
我精简了一下,方便有需要的直接上手
from marshmallow.fields import Raw
...
@app.post("/icon")
@input({"icon": Raw(type="file", required=True)}, location="files")
@doc(description="上传头像")
def icon(data):
print(data.get("icon"))
return {"msg": 'ok'}
除此之外,我还发现一个 apispec 的bug – 无法上传文件。
在测试上述代码的时候我通过apiflask内置的apispec进行调试,发现后端无法获取到文件,转而使用postman。由此发现的这个bug(apispec无法上传,postman正常)
关于文件上传,一开始就支持文件上传,但不支持OpenAPI生成,详见李大的回答: