这里主要是对之前搭建的一个 Flask Restful 代码库(Flask 搭建 Restful 风格项目扩展)进行一个小的改进,主要包括以下方面
- 为接口自动生成 swagger 使用文档
- 使用 Namespace 管理 API 接口
- 为项目添加适配 lingma IDE 的项目提示词
最终项目目录
markdown
.
├── app
│ ├── __init__.py
│ ├── api
│ │ ├── __init__.py
│ │ ├── common
│ │ │ └── utils.py
│ │ ├── models
│ │ │ ├── __init__.py
│ │ │ ├── revoked_token.py
│ │ │ └── user.py
│ │ ├── resources
│ │ │ ├── __init__.py
│ │ │ ├── auth
│ │ │ │ ├── __init__.py
│ │ │ │ ├── auth_api_model.py
│ │ │ │ ├── login.py
│ │ │ │ ├── logout.py
│ │ │ │ └── register.py
│ │ │ └── user
│ │ │ ├── __init__.py
│ │ │ ├── user_api_model.py
│ │ │ └── user.py
│ │ └── schema
│ │ └── register_sha.py
│ ├── config.py
│ └── manage.py
├── LICENSE
├── README.md
├── requirements.txt
└── run.py
需要完成以上优化,首先我们需要将 Flask-RESTful
更改为 flask-restx
pip install flask-restx==1.3.0
,版本可按需更改
使用 Namespace 管理 API 接口
- 引入
flask_restx
中的Namespace
- 创建我们需要的 Namespace 对象
- 通过 Namespace 对象来添加 Resource
未使用 Namespace
python
# app/api/__init__.py
from flask import Blueprint
from flask_restful import Api
from .resources.register import Register
from .resources.login import Login
from .resources.logout import Logout
from .resources.user import UserService
api_blueprint = Blueprint('api', __name__, url_prefix='/api')
api = Api(api_blueprint)
api.add_resource(Register, '/register')
api.add_resource(Login, '/login')
# api.add_resource(Login, '/login', '/refresh', '/test') # 可以添加多个路由
api.add_resource(Logout, '/logout')
api.add_resource(UserService, '/user')
使用 Namespace
python
# app/api/__init__.py
from flask import Blueprint
from flask_restx import Api, Namespace
# 创建API蓝图
api_blueprint = Blueprint('api', __name__, url_prefix='/api')
# 创建Flask-RESTX的Api实例
api = Api(
api_blueprint,
version='1.0',
title='Flask RESTful API',
description='基于Flask-RESTX的RESTful API',
doc='/docs/', # Swagger文档路径
authorizations={
'Bearer': {
'type': 'apiKey',
'in': 'header',
'name': 'Authorization',
'description': 'JWT Token格式: Bearer <token>'
}
},
security='Bearer'
)
auth_ns = Namespace('auth', description='认证相关接口', path='/auth')
users_ns = Namespace('users', description='用户管理接口', path='/users')
from app.api.resources.auth.login import Login
from app.api.resources.auth.logout import Logout
from app.api.resources.auth.register import Register
from app.api.resources.user.user import UserService
auth_ns.add_resource(Register, '/register')
auth_ns.add_resource(Login, '/login')
auth_ns.add_resource(Logout, '/logout')
users_ns.add_resource(UserService, '/profile')
# 注册命名空间到API
api.add_namespace(auth_ns)
api.add_namespace(users_ns)
对于以上代码,需要注意:
如果我们将 Namespace
的定义放到导入语句之后,如下所示:
python
from app.api.resources.auth.login
import Login from app.api.resources.auth.logout
import Logout from app.api.resources.auth.register
import Register from app.api.resources.user.user
import UserService
auth_ns = Namespace('auth', description='认证相关接口', path='/auth')
users_ns = Namespace('users', description='用户管理接口', path='/users')
- 导入模块中引用
auth_ns
会导致错误:Login
、Logout
等类所在的模块中使用了auth_ns
(例如注册资源@auth_ns.route()
),而auth_ns
在导入之后才定义,那么会抛出NameError
,因为此时auth_ns
还未定义。
- 循环引用风险:
- 如果
Login
类所在的模块又导入了app.api.__init__
中的某些内容(如auth_ns
),就可能形成循环依赖(A 导入 B,B 又导入 A)。
- 如果
为接口自动生成 swagger 使用文档
- 请求与响应模型的定义声明
python
from app.api import auth_ns
from flask_restx import fields
# from app.api.common.utils import api_response
# 定义API模型
# 用户注册/登录请求模型
user_credentials = auth_ns.model('UserCredentials', {
'username': fields.String(required=True, description='用户名', example='admin'),
'password': fields.String(required=True, description='密码', example='admin')
})
# 用户注册/登录响应模型
# 登录成功响应模型
login_response_data = auth_ns.model('LoginResponseData', {
'access_token': fields.String(description='访问令牌'),
'refresh_token': fields.String(description='刷新令牌'),
'exp': fields.Integer(description='令牌过期时间戳(毫秒)')
})
login_response = auth_ns.model('LoginResponse', {
'success': fields.Boolean(description='操作是否成功'),
'message': fields.String(description='响应消息'),
'data': fields.Nested(login_response_data, description='登录响应数据')
})
# 登录响应模型(继承api_response)
# login_response = auth_ns.inherit('LoginResponse', api_response, {
# 'data': fields.Nested(login_response_data, description='登录响应数据')
# })
# 使用 clone 方法复制 api_response,并修改 data 字段
# login_response = auth_ns.clone('LoginResponse', api_response, {
# 'data': fields.Nested(login_response_data, description='登录响应数据')
# })
# login_response = auth_ns.clone('LoginResponse', api_response)
# login_response['data'] = fields.Nested(login_response_data, description='登录响应数据')
注意:
auth_ns.inherit
这个是想实现一个类似于面向对象编程中的 class 继承效果的,考虑到可能会有一些通用响应的对象格式,比如
python
api_response = api.model('ApiResponse', {
'success': fields.Boolean(description='操作是否成功'),
'message': fields.String(description='响应消息'),
# 'data': fields.Raw(description='响应数据'),
# 'code': fields.Integer(description='响应状态码')
})
在auth_ns.inherit
使用时不能重写 qpi_response
中的属性,可以使用 auth_ns.clone
来实现
python
# 使用 clone 方法复制 api_response,并修改 data 字段
login_response = auth_ns.clone('LoginResponse', api_response, {
'data': fields.Nested(login_response_data, description='登录响应数据')
})
# login_response = auth_ns.clone('LoginResponse', api_response)
# login_response['data'] = fields.Nested(login_response_data, description='登录响应数据')
- 接口注解添加
@auth_ns.expect(user_credentials)
:
- 指定该接口期望的输入参数格式。这里的 user_credentials 是一个定义好的请求参数模型,通常包含用户名和密码等字段。
- 在生成的 Swagger 文档中,会显示该接口需要的请求体格式,并进行参数校验。
@auth_ns.marshal_with(login_response)
:
- 指定该接口返回的数据格式。login_response 是一个定义好的响应模型,通常包含登录成功后的用户信息和 Token。
- 确保返回给客户端的数据结构一致,并且可以根据需求选择性地返回部分字段。
@auth_ns.doc(description='用户登录接口', responses={...})
:
- 为该接口添加详细的文档描述和响应码说明。
description
:提供接口的功能描述,帮助开发者理解该接口的作用。responses
:定义不同 HTTP 状态码对应的响应描述,例如 200 表示登录成功,400 表示用户不存在等。- 在 Swagger UI 中展示更详细的接口文档,方便测试和使用。
python
# ...
from app.api import auth_ns
from .auth_api_model import user_credentials, login_response
class Login(Resource):
@auth_ns.expect(user_credentials)
@auth_ns.marshal_with(login_response)
@auth_ns.doc(description='用户登录接口',
responses={
200: '登录成功',
400: '用户不存在',
401: '密码错误',
500: '服务器内部错误'
})
def post(self):
pass
@jwt_required(refresh=True)
@auth_ns.doc(description='用户token 刷新接口',
responses={
200: '登录成功',
500: '服务器内部错误'
})
@auth_ns.marshal_with(login_response)
def get(self):
pass
- 测试
可以通过 http://127.0.0.1:5003/api/docs/
本地查看 swagger 文档,并调试
为项目添加适配 lingma IDE 的项目提示词
规则 vscode 也适配
详细可参考 lingma规则设置