Flask 搭建 Restful 风格项目扩展(Namespace、Swagger)

这里主要是对之前搭建的一个 Flask Restful 代码库(Flask 搭建 Restful 风格项目扩展)进行一个小的改进,主要包括以下方面

  1. 为接口自动生成 swagger 使用文档
  2. 使用 Namespace 管理 API 接口
  3. 为项目添加适配 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 接口

  1. 引入 flask_restx中的 Namespace
  2. 创建我们需要的 Namespace 对象
  3. 通过 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')
  1. 导入模块中引用 auth_ns 会导致错误:
    • LoginLogout 等类所在的模块中使用了 auth_ns(例如注册资源 @auth_ns.route()),而 auth_ns 在导入之后才定义,那么会抛出 NameError,因为此时 auth_ns 还未定义。
  2. 循环引用风险:
    • 如果 Login 类所在的模块又导入了 app.api.__init__ 中的某些内容(如 auth_ns),就可能形成循环依赖(A 导入 B,B 又导入 A)。

为接口自动生成 swagger 使用文档

  1. 请求与响应模型的定义声明
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='登录响应数据')
  1. 接口注解添加

@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
  1. 测试

可以通过 http://127.0.0.1:5003/api/docs/ 本地查看 swagger 文档,并调试

为项目添加适配 lingma IDE 的项目提示词

规则 vscode 也适配

详细可参考 lingma规则设置

相关推荐
Max8124 小时前
Agno Agent 服务端文件上传处理机制
后端
调试人生的显微镜4 小时前
苹果 App 怎么上架?从开发到发布的完整流程与使用 开心上架 跨平台上传
后端
顾漂亮4 小时前
Spring AOP 实战案例+避坑指南
java·后端·spring
间彧4 小时前
Redis Stream相比阻塞列表和发布订阅有哪些优势?适合什么场景?
后端
间彧4 小时前
Redis阻塞弹出和发布订阅模式有什么区别?各自适合什么场景?
后端
苏三说技术4 小时前
统计接口耗时的6种常见方法
后端
SimonKing4 小时前
Mybatis-Plus的竞争对手来了,试试 MyBatis-Flex
java·后端·程序员
我命由我123455 小时前
PDFBox - PDFBox 加载 PDF 异常清单(数据为 null、数据为空、数据异常、文件为 null、文件不存在、文件异常)
java·服务器·后端·java-ee·pdf·intellij-idea·intellij idea
渣哥5 小时前
当容器里有多个 Bean,@Qualifier 如何精准定位?
javascript·后端·面试
7哥♡ۣۖᝰꫛꫀꪝۣℋ5 小时前
Spring Boot
java·spring boot·后端