08-Flask RESTful API设计与实现深度解析

18【高级指南】Flask RESTful API开发:从入门到精通的完整实战攻略 本文详细介绍了如何使用Flask开发RESTful API,从基础概念到高级实践,涵盖了API开发的核心技能。首先,文章解释了REST架构风格的基本原则,包括资源、表现层和状态转移,并展示了HTTP方法与CRUD操作的对应关系及常用状态码。接着,文章对比了Flask原生开发与使用扩展(如Flask-RESTful和Flask-RESTX)的不同方式,提供了代码示例,并给出了不同场景下的工具选择建议。最后,文章探讨了RESTful路由设计的最佳实践,包括版本化、资源组织和使用蓝图进行模块化管理。通过本文,开发 阅读详情

Flask RESTful API设计与实现深度解析

一、概述

RESTful API是现代Web应用的核心接口,Flask通过Flask-RESTful扩展提供了强大的API开发能力。本文将深入解析RESTful设计原则、资源路由、请求解析、响应格式、错误处理、API文档、认证授权以及性能优化,帮助开发者构建规范、高效、易维护的API系统。

二、RESTful设计原则

2.1 REST架构约束

"""
REST (Representational State Transfer) 架构风格核心约束:

1. 客户端-服务器分离 (Client-Server)
   - 关注点分离,提高可移植性

2. 无状态 (Stateless)
   - 每个请求包含所有必要信息
   - 服务器不存储客户端上下文

3. 可缓存 (Cacheable)
   - 响应必须标识是否可缓存
   - 提高网络效率

4. 统一接口 (Uniform Interface)
   - 资源标识 (URI)
   - 通过表述操作资源
   - 自描述消息
   - HATEOAS (超媒体作为应用状态引擎)

5. 分层系统 (Layered System)
   - 客户端无法判断连接的是终端服务器还是中间层

6. 按需代码 (Code on Demand) - 可选
   - 服务器可以扩展客户端功能
"""

# RESTful API设计规范
class RESTfulGuidelines:
    """RESTful API设计指南"""
    
    # URL设计规范
    URL_RULES = {
        # 使用名词而非动词
        'correct': [
            'GET /users',          # 获取用户列表
            'GET /users/1',        # 获取单个用户
            'POST /users',         # 创建用户
            'PUT /users/1',        # 更新用户(完整)
            'PATCH /users/1',      # 更新用户(部分)
            'DELETE /users/1',     # 删除用户
        ],
        'incorrect': [
            'GET /getUsers',
            'POST /createUser',
            'POST /deleteUser/1',
        ],
        
        # 使用复数形式
        'plural': [
            '/users', '/posts', '/comments', '/products'
        ],
        'singular': [
            '/user', '/post', '/comment', '/product'
        ],
        
        # 表达资源关系
        'relationships': [
            'GET /users/1/posts',           # 用户的文章列表
            'GET /users/1/posts/2',         # 用户的特定文章
            'POST /users/1/posts',          # 为用户创建文章
            'GET /posts/2/comments',        # 文章的评论列表
        ],
        
        # 过滤、排序、分页
        'query_params': [
            'GET /users?status=active&role=admin',  # 过滤
            'GET /users?sort=created_at:desc',      # 排序
            'GET /users?page=1&per_page=20',        # 分页
            'GET /users?fields=id,name,email',      # 字段选择
            'GET /users?embed=posts,comments',      # 嵌入关联资源
        ],
    }
    
    # HTTP方法语义
    HTTP_METHODS = {
        'GET': {
            'description': '获取资源',
            'safe': True,           # 是否安全(不修改资源)
            'idempotent': True,     # 是否幂等
            'cacheable': True,      # 是否可缓存
        },
        'POST': {
            'description': '创建资源',
            'safe': False,
            'idempotent': False,
            'cacheable': False,
        },
        'PUT': {
            'description': '完整更新资源',
            'safe': False,
            'idempotent': True,
            'cacheable': False,
        },
        'PATCH': {
            'description': '部分更新资源',
            'safe': False,
            'idempotent': True,     # 实现正确时幂等
            'cacheable': False,
        },
        'DELETE': {
            'description': '删除资源',
            'safe': False,
            'idempotent': True,
            'cacheable': False,
        },
        'HEAD': {
            'description': '获取资源元数据',
            'safe': True,
            'idempotent': True,
            'cacheable': True,
        },
        'OPTIONS': {
            'description': '获取资源支持的HTTP方法',
            'safe': True,
            'idempotent': True,
            'cacheable': False,
        },
    }
    
    # HTTP状态码
    STATUS_CODES = {
        # 2xx 成功
        200: 'OK - 请求成功',
        201: 'Created - 资源创建成功',
        202: 'Accepted - 请求已接受,处理中',
        204: 'No Content - 成功但无返回内容',
        
        # 3xx 重定向
        301: 'Moved Permanently - 资源已永久移动',
        304: 'Not Modified - 资源未修改(缓存有效)',
        
        # 4xx 客户端错误
        400: 'Bad Request - 请求格式错误',
        401: 'Unauthorized - 未认证',
        403: 'Forbidden - 无权限',
        404: 'Not Found - 资源不存在',
        405: 'Method Not Allowed - 方法不允许',
        406: 'Not Acceptable - 无法生成请求的媒体类型',
        409: 'Conflict - 资源冲突',
        410: 'Gone - 资源已删除',
        415: 'Unsupported Media Type - 不支持的媒体类型',
        422: 'Unprocessable Entity - 语义错误',
        429: 'Too Many Requests - 请求过多',
        
        # 5xx 服务器错误
        500: 'Internal Server Error - 服务器内部错误',
        501: 'Not Implemented - 功能未实现',
        502: 'Bad Gateway - 网关错误',
        503: 'Service Unavailable - 服务不可用',
        504: 'Gateway Timeout - 网关超时',
    }

2.2 RESTful API架构图

请求处理流程

失败

成功

失败

成功

失败

成功

成功

失败

请求

认证检查

401 Unauthorized

权限检查

403 Forbidden

参数验证

400 Bad Request

执行业务逻辑

执行结果

200/201/204

500 Internal Error

RESTful API架构

HTTP请求

认证/限流

授权

路由分发

业务逻辑

数据访问

CRUD

响应

JSON

客户端

API网关

认证层

路由层

资源层

服务层

数据访问层

数据库

序列化器

三、Flask-RESTful基础

3.1 扩展配置

from flask import Flask
from flask_restful import Api, Resource, fields, marshal_with, reqparse

app = Flask(__name__)

# Flask-RESTful配置
app.config['RESTFUL_JSON'] = {
    'ensure_ascii': False,  # 支持中文
    'indent': 2,            # 格式化输出
    'sort_keys': True,      # 排序键
}

# 初始化API
api = Api(app, prefix='/api/v1', catch_all_404s=True)

# 或使用工厂模式
def create_api(app):
    api = Api(app)
    api.prefix = '/api/v1'
    return api

3.2 资源类定义

from flask_restful import Resource, fields, marshal_with, reqparse, abort
from models import User, db
from flask_jwt_extended import jwt_required, get_jwt_identity

# 请求解析器
user_parser = reqparse.RequestParser()
user_parser.add_argument('username', type=str, required=True, 
                         help='用户名不能为空', location='json')
user_parser.add_argument('email', type=str, required=True, 
                         help='邮箱不能为空', location='json')
user_parser.add_argument('password', type=str, required=True, 
                         help='密码不能为空', location='json')
user_parser.add_argument('name', type=str, location='json')
user_parser.add_argument('avatar', type=str, location='json')

# 更新解析器(部分字段可选)
user_update_parser = reqparse.RequestParser()
user_update_parser.add_argument('name', type=str, location='json')
user_update_parser.add_argument('avatar', type=str, location='json')
user_update_parser.add_argument('bio', type=str, location='json')

# 查询解析器
user_query_parser = reqparse.RequestParser()
user_query_parser.add_argument('page', type=int, default=1, location='args')
user_query_parser.add_argument('per_page', type=int, default=20, location='args')
user_query_parser.add_argument('status', type=str, choices=['active', 'inactive'], 
                               location='args')
user_query_parser.add_argument('sort', type=str, default='created_at:desc', 
                               location='args')
user_query_parser.add_argument('search', type=str, location='args')

# 序列化字段
user_fields = {
    'id': fields.Integer,
    'username': fields.String,
    'email': fields.String,
    'name': fields.String,
    'avatar': fields.String(attribute='avatar_url'),
    'bio': fields.String,
    'active': fields.Boolean,
    'created_at': fields.DateTime(dt_format='iso8601'),
    'updated_at': fields.DateTime(dt_format='iso8601'),
    '_links': fields.Nested({
        'self': fields.Url('api.user', absolute=True),
        'posts': fields.Url('api.user_posts', absolute=True),
    })
}

user_list_fields = {
    'items': fields.List(fields.Nested(user_fields)),
    'pagination': fields.Nested({
        'page': fields.Integer,
        'per_page': fields.Integer,
        'total': fields.Integer,
        'pages': fields.Integer,
        'has_next': fields.Boolean,
        'has_prev': fields.Boolean,
    }),
    '_links': fields.Nested({
        'self': fields.Url('api.users', absolute=True),
        'next': fields.String,
        'prev': fields.String,
    })
}


class UserListResource(Resource):
    """用户列表资源"""
    
    method_decorators = [jwt_required()]  # 类级别装饰器
    
    @marshal_with(user_list_fields)
    def get(self):
        """获取用户列表"""
        args = user_query_parser.parse_args()
        
        # 构建查询
        query = User.query
        
        # 过滤
        if args['status']:
            query = query.filter(User.active == (args['status'] == 'active'))
        
        # 搜索
        if args['search']:
            search_term = f"%{args['search']}%"
            query = query.filter(
                (User.username.ilike(search_term)) |
                (User.email.ilike(search_term)) |
                (User.name.ilike(search_term))
            )
        
        # 排序
        sort_field, sort_order = args['sort'].split(':')
        sort_column = getattr(User, sort_field, User.created_at)
        if sort_order == 'desc':
            sort_column = sort_column.desc()
        query = query.order_by(sort_column)
        
        # 分页
        pagination = query.paginate(
            page=args['page'],
            per_page=min(args['per_page'], 100),  # 限制最大每页数量
            error_out=False
        )
        
        # 构建响应
        result = {
            'items': pagination.items,
            'pagination': {
                'page': pagination.page,
                'per_page': pagination.per_page,
                'total': pagination.total,
                'pages': pagination.pages,
                'has_next': pagination.has_next,
                'has_prev': pagination.has_prev,
            },
            '_links': {
                'self': None,  # 由marshal_with填充
                'next': url_for('api.users', page=pagination.next_num, 
                               _external=True) if pagination.has_next else None,
                'prev': url_for('api.users', page=pagination.prev_num, 
                               _external=True) if pagination.has_prev else None,
            }
        }
        
        return result
    
    @marshal_with(user_fields)
    def post(self):
        """创建用户"""
        args = user_parser.parse_args()
        
        # 检查用户名是否已存在
        if User.query.filter_by(username=args['username']).first():
            abort(409, message='用户名已存在')
        
        # 检查邮箱是否已存在
        if User.query.filter_by(email=args['email']).first():
            abort(409, message='邮箱已存在')
        
        # 创建用户
        user = User(
            username=args['username'],
            email=args['email'],
            name=args.get('name'),
            avatar=args.get('avatar'),
        )
        user.set_password(args['password'])
        
        db.session.add(user)
        db.session.commit()
        
        return user, 201


class UserResource(Resource):
    """单个用户资源"""
    
    method_decorators = [jwt_required()]
    
    @marshal_with(user_fields)
    def get(self, user_id):
        """获取单个用户"""
        user = User.query.get_or_404(user_id)
        return user
    
    @marshal_with(user_fields)
    def put(self, user_id):
        """完整更新用户"""
        current_user_id = get_jwt_identity()
        
        # 权限检查
        if current_user_id != user_id:
            abort(403, message='无权修改其他用户信息')
        
        user = User.query.get_or_404(user_id)
        args = user_parser.parse_args()
        
        # 更新所有字段
        user.username = args['username']
        user.email = args['email']
        user.name = args.get('name')
        user.avatar = args.get('avatar')
        if args['password']:
            user.set_password(args['password'])
        
        db.session.commit()
        return user
    
    @marshal_with(user_fields)
    def patch(self, user_id):
        """部分更新用户"""
        current_user_id = get_jwt_identity()
        
        if current_user_id != user_id:
            abort(403, message='无权修改其他用户信息')
        
        user = User.query.get_or_404(user_id)
        args = user_update_parser.parse_args()
        
        # 只更新提供的字段
        if args['name'] is not None:
            user.name = args['name']
        if args['avatar'] is not None:
            user.avatar = args['avatar']
        if args['bio'] is not None:
            user.bio = args['bio']
        
        db.session.commit()
        return user
    
    def delete(self, user_id):
        """删除用户"""
        current_user_id = get_jwt_identity()
        
        if current_user_id != user_id:
            abort(403, message='无权删除其他用户')
        
        user = User.query.get_or_404(user_id)
        db.session.delete(user)
        db.session.commit()
        
        return '', 204


# 注册资源路由
api.add_resource(UserListResource, '/users', endpoint='users')
api.add_resource(UserResource, '/users/<int:user_id>', endpoint='user')

3.3 嵌套资源

# 文章序列化字段
post_fields = {
    'id': fields.Integer,
    'title': fields.String,
    'content': fields.String,
    'summary': fields.String,
    'status': fields.String,
    'view_count': fields.Integer,
    'created_at': fields.DateTime(dt_format='iso8601'),
    'updated_at': fields.DateTime(dt_format='iso8601'),
    'author': fields.Nested(user_fields, attribute='user'),
    '_links': fields.Nested({
        'self': fields.Url('api.post', absolute=True),
        'author': fields.Url('api.user', absolute=True),
        'comments': fields.Url('api.post_comments', absolute=True),
    })
}

# 文章解析器
post_parser = reqparse.RequestParser()
post_parser.add_argument('title', type=str, required=True, location='json')
post_parser.add_argument('content', type=str, required=True, location='json')
post_parser.add_argument('summary', type=str, location='json')
post_parser.add_argument('status', type=str, choices=['draft', 'published', 'archived'],
                        default='draft', location='json')


class UserPostsResource(Resource):
    """用户的文章列表"""
    
    method_decorators = [jwt_required()]
    
    @marshal_with(fields.List(fields.Nested(post_fields)))
    def get(self, user_id):
        """获取用户的文章列表"""
        user = User.query.get_or_404(user_id)
        return user.posts.filter(Post.status == 'published').all()
    
    @marshal_with(post_fields)
    def post(self, user_id):
        """为用户创建文章"""
        current_user_id = get_jwt_identity()
        
        if current_user_id != user_id:
            abort(403, message='无权为其他用户创建文章')
        
        user = User.query.get_or_404(user_id)
        args = post_parser.parse_args()
        
        post = Post(
            title=args['title'],
            content=args['content'],
            summary=args.get('summary'),
            status=args['status'],
            user_id=user_id
        )
        
        db.session.add(post)
        db.session.commit()
        
        return post, 201


# 注册嵌套资源路由
api.add_resource(UserPostsResource, '/users/<int:user_id>/posts', endpoint='user_posts')

四、请求解析与验证

4.1 RequestParser详解

from flask_restful import reqparse

# 基本用法
parser = reqparse.RequestParser()

# 参数类型
parser.add_argument('name', type=str)                    # 字符串
parser.add_argument('age', type=int)                     # 整数
parser.add_argument('price', type=float)                 # 浮点数
parser.add_argument('active', type=bool)                 # 布尔值
parser.add_argument('tags', type=list, location='json')  # 列表
parser.add_argument('settings', type=dict, location='json')  # 字典

# 参数位置
parser.add_argument('query_param', location='args')      # URL查询参数
parser.add_argument('form_param', location='form')       # 表单数据
parser.add_argument('json_param', location='json')       # JSON请求体
parser.add_argument('header_param', location='headers')  # 请求头
parser.add_argument('cookie_param', location='cookies')  # Cookie
parser.add_argument('file_param', location='files')      # 上传文件

# 必填验证
parser.add_argument('required_field', required=True, 
                    help='此字段为必填项')

# 默认值
parser.add_argument('page', type=int, default=1)
parser.add_argument('per_page', type=int, default=20)

# 选择验证
parser.add_argument('status', type=str, 
                    choices=['active', 'inactive', 'pending'],
                    help='状态必须是 active, inactive 或 pending')

# 正则验证
import re
parser.add_argument('phone', type=str,
                    help='请输入有效的手机号码')

# 自定义类型验证
def email_type(value):
    """邮箱类型验证"""
    if '@' not in value:
        raise ValueError('请输入有效的邮箱地址')
    return value

parser.add_argument('email', type=email_type)

# 自定义验证函数
def validate_password(value):
    """密码验证"""
    if len(value) < 8:
        raise ValueError('密码长度至少8位')
    if not re.search(r'[A-Z]', value):
        raise ValueError('密码必须包含大写字母')
    if not re.search(r'[a-z]', value):
        raise ValueError('密码必须包含小写字母')
    if not re.search(r'\d', value):
        raise ValueError('密码必须包含数字')
    return value

parser.add_argument('password', type=validate_password)

# 忽略未知参数
parser.add_argument('known_param', type=str)
args = parser.parse_args(strict=True)  # 严格模式,拒绝未知参数

# 继承解析器
base_parser = reqparse.RequestParser()
base_parser.add_argument('page', type=int, default=1)
base_parser.add_argument('per_page', type=int, default=20)

search_parser = base_parser.copy()
search_parser.add_argument('keyword', type=str)
search_parser.add_argument('sort', type=str)

4.2 高级验证

from werkzeug.datastructures import FileStorage
from flask_restful import inputs

# 使用内置验证器
parser = reqparse.RequestParser()

# 日期验证
parser.add_argument('date', type=inputs.date, 
                    help='日期格式: YYYY-MM-DD')

# 日期时间验证
parser.add_argument('datetime', type=inputs.datetime_from_iso8601,
                    help='日期时间格式: ISO 8601')

# URL验证
parser.add_argument('website', type=inputs.url,
                    help='请输入有效的URL')

# 正则验证
parser.add_argument('username', type=inputs.regex('^[a-zA-Z][a-zA-Z0-9_]{2,19}$'),
                    help='用户名必须以字母开头,3-20个字符,只能包含字母、数字和下划线')

# 布尔值验证(支持多种格式)
parser.add_argument('active', type=inputs.boolean,
                    help='请输入 true 或 false')

# 整数范围验证
parser.add_argument('rating', type=inputs.int_range(1, 5),
                    help='评分必须在1-5之间')

# 文件上传验证
def file_type(allowed_extensions):
    """文件类型验证器"""
    def validate(file):
        if not isinstance(file, FileStorage):
            raise ValueError('请上传文件')
        
        filename = file.filename
        if '.' not in filename:
            raise ValueError('文件必须有扩展名')
        
        ext = filename.rsplit('.', 1)[1].lower()
        if ext not in allowed_extensions:
            raise ValueError(f'只允许以下文件类型: {", ".join(allowed_extensions)}')
        
        return file
    return validate

parser.add_argument('avatar', type=file_type(['jpg', 'jpeg', 'png', 'gif']),
                    location='files')

# 列表验证
def list_of_ints(value):
    """整数列表验证"""
    if isinstance(value, str):
        value = value.split(',')
    return [int(x) for x in value]

parser.add_argument('ids', type=list_of_ints, location='args')

# JSON Schema验证
import jsonschema

def validate_json_schema(schema):
    """JSON Schema验证器"""
    def validate(value):
        try:
            jsonschema.validate(value, schema)
        except jsonschema.ValidationError as e:
            raise ValueError(f'JSON验证失败: {e.message}')
        return value
    return validate

address_schema = {
    'type': 'object',
    'properties': {
        'province': {'type': 'string'},
        'city': {'type': 'string'},
        'address': {'type': 'string'},
        'postal_code': {'type': 'string', 'pattern': r'^\d{6}$'}
    },
    'required': ['province', 'city', 'address']
}

parser.add_argument('address', type=validate_json_schema(address_schema),
                    location='json')

五、响应格式与序列化

5.1 序列化字段详解

from flask_restful import fields, marshal, marshal_with

# 基本字段类型
user_fields = {
    'id': fields.Integer,                           # 整数
    'username': fields.String,                      # 字符串
    'email': fields.String,                         # 字符串
    'active': fields.Boolean,                       # 布尔值
    'balance': fields.Float,                        # 浮点数
    'price': fields.Fixed(decimals=2),              # 定点数(保留小数位)
    'created_at': fields.DateTime(dt_format='iso8601'),  # 日期时间
    'birthday': fields.Date,                        # 日期
    'login_time': fields.Time,                      # 时间
    'settings': fields.Raw,                         # 原始数据(不转换)
    'count': fields.Integer(default=0),             # 默认值
}

# 格式化字段
formatted_fields = {
    'price': fields.Price,                          # 价格格式化
    'amount': fields.FormattedString('¥{0:.2f}'),   # 自定义格式化
    'percentage': fields.FormattedString('{0:.1%}'), # 百分比格式
}

# 属性映射
user_fields = {
    'id': fields.Integer,
    'name': fields.String(attribute='username'),    # 映射到username属性
    'avatar_url': fields.String(attribute='avatar'), # 重命名属性
    'full_name': fields.String(attribute='get_full_name'),  # 映射到方法
}

# 嵌套字段
post_fields = {
    'id': fields.Integer,
    'title': fields.String,
    'content': fields.String,
    'author': fields.Nested(user_fields, attribute='user'),  # 嵌套对象
    'tags': fields.List(fields.String),              # 字符串列表
    'comments': fields.List(fields.Nested(comment_fields)),  # 对象列表
}

# URL字段
post_fields = {
    'id': fields.Integer,
    'title': fields.String,
    'url': fields.Url('api.post', absolute=True),   # 绝对URL
    'relative_url': fields.Url('api.post', absolute=False),  # 相对URL
}

# 自定义字段
class EnumField(fields.Raw):
    """枚举字段"""
    def __init__(self, enum_class, **kwargs):
        super().__init__(**kwargs)
        self.enum_class = enum_class
    
    def format(self, value):
        if isinstance(value, self.enum_class):
            return value.value
        return value


class FileSizeField(fields.Raw):
    """文件大小格式化字段"""
    def format(self, value):
        for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
            if value < 1024:
                return f'{value:.2f} {unit}'
            value /= 1024
        return f'{value:.2f} PB'


class MaskedEmailField(fields.Raw):
    """脱敏邮箱字段"""
    def format(self, value):
        if not value or '@' not in value:
            return value
        local, domain = value.split('@', 1)
        masked_local = local[0] + '***' + local[-1] if len(local) > 2 else local[0] + '***'
        return f'{masked_local}@{domain}'


# 使用自定义字段
user_fields = {
    'id': fields.Integer,
    'email': MaskedEmailField(),
    'file_size': FileSizeField(attribute='file_size_bytes'),
    'status': EnumField(UserStatus),
}

5.2 响应构建

from flask import jsonify, make_response
from flask_restful import marshal

class ResponseBuilder:
    """响应构建器"""
    
    @staticmethod
    def success(data=None, message='操作成功', status_code=200):
        """成功响应"""
        response = {
            'success': True,
            'message': message,
        }
        if data is not None:
            response['data'] = data
        return response, status_code
    
    @staticmethod
    def error(message='操作失败', status_code=400, errors=None):
        """错误响应"""
        response = {
            'success': False,
            'message': message,
        }
        if errors:
            response['errors'] = errors
        return response, status_code
    
    @staticmethod
    def paginated(items, pagination, fields):
        """分页响应"""
        return {
            'items': marshal(items, fields),
            'pagination': {
                'page': pagination.page,
                'per_page': pagination.per_page,
                'total': pagination.total,
                'pages': pagination.pages,
                'has_next': pagination.has_next,
                'has_prev': pagination.has_prev,
            },
            '_links': {
                'self': url_for(request.endpoint, page=pagination.page, **request.args),
                'next': url_for(request.endpoint, page=pagination.next_num, **request.args) if pagination.has_next else None,
                'prev': url_for(request.endpoint, page=pagination.prev_num, **request.args) if pagination.has_prev else None,
            }
        }
    
    @staticmethod
    def created(data, location=None):
        """创建成功响应"""
        response = make_response(jsonify(data), 201)
        if location:
            response.headers['Location'] = location
        return response
    
    @staticmethod
    def no_content():
        """无内容响应"""
        return '', 204


# 使用示例
class PostResource(Resource):
    
    @marshal_with(post_fields)
    def get(self, post_id):
        post = Post.query.get_or_404(post_id)
        return ResponseBuilder.success(post)
    
    def delete(self, post_id):
        post = Post.query.get_or_404(post_id)
        db.session.delete(post)
        db.session.commit()
        return ResponseBuilder.no_content()

六、错误处理

6.1 统一错误处理

from flask_restful import abort
from werkzeug.exceptions import HTTPException
import traceback

# 自定义异常
class APIError(Exception):
    """API基础异常"""
    def __init__(self, message, status_code=400, errors=None):
        super().__init__(message)
        self.message = message
        self.status_code = status_code
        self.errors = errors


class ValidationError(APIError):
    """验证错误"""
    def __init__(self, message='数据验证失败', errors=None):
        super().__init__(message, 422, errors)


class NotFoundError(APIError):
    """资源不存在"""
    def __init__(self, message='资源不存在'):
        super().__init__(message, 404)


class ForbiddenError(APIError):
    """禁止访问"""
    def __init__(self, message='无权访问'):
        super().__init__(message, 403)


class ConflictError(APIError):
    """资源冲突"""
    def __init__(self, message='资源冲突'):
        super().__init__(message, 409)


# 错误处理器
@app.errorhandler(APIError)
def handle_api_error(error):
    """处理API异常"""
    response = {
        'success': False,
        'message': error.message,
    }
    if error.errors:
        response['errors'] = error.errors
    return jsonify(response), error.status_code


@app.errorhandler(HTTPException)
def handle_http_error(error):
    """处理HTTP异常"""
    response = {
        'success': False,
        'message': error.description,
        'status_code': error.code,
    }
    return jsonify(response), error.code


@app.errorhandler(Exception)
def handle_generic_error(error):
    """处理通用异常"""
    # 记录错误日志
    app.logger.error(f'Unhandled exception: {str(error)}\n{traceback.format_exc()}')
    
    response = {
        'success': False,
        'message': '服务器内部错误',
        'status_code': 500,
    }
    
    # 开发环境返回详细错误信息
    if app.debug:
        response['debug'] = {
            'type': type(error).__name__,
            'message': str(error),
            'traceback': traceback.format_exc().split('\n')
        }
    
    return jsonify(response), 500


# 数据库错误处理
from sqlalchemy.exc import IntegrityError, SQLAlchemyError

@app.errorhandler(IntegrityError)
def handle_integrity_error(error):
    """处理数据库完整性错误"""
    response = {
        'success': False,
        'message': '数据完整性错误',
    }
    
    # 解析错误信息
    error_msg = str(error.orig)
    if 'unique constraint' in error_msg.lower():
        response['message'] = '数据已存在,请检查唯一字段'
    elif 'foreign key constraint' in error_msg.lower():
        response['message'] = '关联数据不存在'
    
    return jsonify(response), 409


@app.errorhandler(SQLAlchemyError)
def handle_sqlalchemy_error(error):
    """处理数据库错误"""
    app.logger.error(f'Database error: {str(error)}')
    return jsonify({
        'success': False,
        'message': '数据库操作失败'
    }), 500

6.2 验证错误处理

from marshmallow import ValidationError as MarshmallowValidationError

class ValidationErrorHandler:
    """验证错误处理器"""
    
    @staticmethod
    def format_errors(errors):
        """格式化错误信息"""
        formatted = {}
        for field, messages in errors.items():
            if isinstance(messages, list):
                formatted[field] = messages
            elif isinstance(messages, dict):
                formatted[field] = ValidationErrorHandler.format_errors(messages)
            else:
                formatted[field] = [str(messages)]
        return formatted
    
    @staticmethod
    def handle_wtforms_errors(form):
        """处理WTForms验证错误"""
        errors = {}
        for field_name, field in form._fields.items():
            if field.errors:
                errors[field_name] = field.errors
        return errors
    
    @staticmethod
    def handle_marshmallow_errors(error):
        """处理Marshmallow验证错误"""
        return ValidationErrorHandler.format_errors(error.messages)


# 使用示例
@app.errorhandler(MarshmallowValidationError)
def handle_marshmallow_validation_error(error):
    """处理Marshmallow验证错误"""
    return jsonify({
        'success': False,
        'message': '数据验证失败',
        'errors': ValidationErrorHandler.format_errors(error.messages)
    }), 422

七、API文档

7.1 Flask-RESTX Swagger文档

from flask_restx import Api, Resource, fields, Namespace

# 初始化API文档
api = Api(
    app,
    version='1.0',
    title='Flask API',
    description='Flask RESTful API文档',
    doc='/api/docs',  # 文档URL
    prefix='/api/v1',
    authorizations={
        'Bearer': {
            'type': 'apiKey',
            'in': 'header',
            'name': 'Authorization',
            'description': 'JWT Token格式: Bearer {token}'
        }
    },
    security='Bearer'
)

# 创建命名空间
ns_users = Namespace('users', description='用户相关操作')
ns_posts = Namespace('posts', description='文章相关操作')

api.add_namespace(ns_users, path='/users')
api.add_namespace(ns_posts, path='/posts')

# 定义模型
user_model = api.model('User', {
    'id': fields.Integer(description='用户ID'),
    'username': fields.String(description='用户名', required=True),
    'email': fields.String(description='邮箱', required=True),
    'name': fields.String(description='姓名'),
    'avatar': fields.String(description='头像URL'),
    'active': fields.Boolean(description='是否激活'),
    'created_at': fields.DateTime(description='创建时间'),
})

user_create_model = api.model('UserCreate', {
    'username': fields.String(description='用户名', required=True, min_length=3, max_length=20),
    'email': fields.String(description='邮箱', required=True),
    'password': fields.String(description='密码', required=True, min_length=8),
    'name': fields.String(description='姓名'),
})

user_update_model = api.model('UserUpdate', {
    'name': fields.String(description='姓名'),
    'avatar': fields.String(description='头像URL'),
    'bio': fields.String(description='个人简介'),
})

pagination_model = api.model('Pagination', {
    'page': fields.Integer(description='当前页'),
    'per_page': fields.Integer(description='每页数量'),
    'total': fields.Integer(description='总数'),
    'pages': fields.Integer(description='总页数'),
    'has_next': fields.Boolean(description='是否有下一页'),
    'has_prev': fields.Boolean(description='是否有上一页'),
})

user_list_model = api.model('UserList', {
    'items': fields.List(fields.Nested(user_model)),
    'pagination': fields.Nested(pagination_model),
})

error_model = api.model('Error', {
    'success': fields.Boolean(description='是否成功'),
    'message': fields.String(description='错误信息'),
    'errors': fields.Raw(description='详细错误'),
})


@ns_users.route('')
class UserList(Resource):
    @ns_users.doc('list_users')
    @ns_users.expect(ns_users.parser().add_argument('page', type=int, default=1, help='页码')
                                .add_argument('per_page', type=int, default=20, help='每页数量')
                                .add_argument('search', type=str, help='搜索关键词'))
    @ns_users.marshal_with(user_list_model)
    @ns_users.response(200, '成功')
    def get(self):
        """获取用户列表"""
        pass
    
    @ns_users.doc('create_user')
    @ns_users.expect(user_create_model, validate=True)
    @ns_users.marshal_with(user_model, code=201)
    @ns_users.response(400, '参数错误', error_model)
    @ns_users.response(409, '用户已存在', error_model)
    def post(self):
        """创建用户"""
        pass


@ns_users.route('/<int:user_id>')
@ns_users.response(404, '用户不存在', error_model)
class UserResource(Resource):
    @ns_users.doc('get_user')
    @ns_users.marshal_with(user_model)
    def get(self, user_id):
        """获取单个用户"""
        pass
    
    @ns_users.doc('update_user')
    @ns_users.expect(user_update_model, validate=True)
    @ns_users.marshal_with(user_model)
    def put(self, user_id):
        """更新用户"""
        pass
    
    @ns_users.doc('delete_user')
    @ns_users.response(204, '删除成功')
    def delete(self, user_id):
        """删除用户"""
        pass

7.2 API文档架构图

客户端支持

文档内容

API文档生成

代码注解

Flask-RESTX

OpenAPI规范

Swagger UI

ReDoc

API客户端生成

API端点列表

请求参数说明

响应格式定义

认证方式

示例代码

JavaScript

Python

Java

Go

八、认证与授权

8.1 JWT认证

from flask_jwt_extended import (
    JWTManager, create_access_token, create_refresh_token,
    jwt_required, get_jwt_identity, get_jwt,
    set_access_cookies, set_refresh_cookies,
    unset_jwt_cookies, get_csrf_token
)

# JWT配置
app.config['JWT_SECRET_KEY'] = 'your-jwt-secret-key'
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(hours=1)
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=30)
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'cookies']
app.config['JWT_COOKIE_SECURE'] = True
app.config['JWT_COOKIE_CSRF_PROTECT'] = True
app.config['JWT_CSRF_IN_COOKIES'] = True

jwt = JWTManager(app)


# JWT回调
@jwt.user_identity_loader
def user_identity_lookup(user):
    """用户身份标识"""
    return user.id


@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
    """用户查找回调"""
    identity = jwt_data['sub']
    return User.query.get(identity)


@jwt.expired_token_loader
def expired_token_callback(jwt_header, jwt_payload):
    """Token过期回调"""
    return jsonify({
        'success': False,
        'message': 'Token已过期',
        'error': 'token_expired'
    }), 401


@jwt.invalid_token_loader
def invalid_token_callback(error_string):
    """无效Token回调"""
    return jsonify({
        'success': False,
        'message': '无效的Token',
        'error': 'invalid_token'
    }), 401


@jwt.unauthorized_loader
def missing_token_callback(error_string):
    """缺少Token回调"""
    return jsonify({
        'success': False,
        'message': '缺少认证Token',
        'error': 'authorization_required'
    }), 401


@jwt.revoked_token_loader
def revoked_token_callback(jwt_header, jwt_payload):
    """Token被撤销回调"""
    return jsonify({
        'success': False,
        'message': 'Token已被撤销',
        'error': 'token_revoked'
    }), 401


# Token黑名单
app.config['JWT_BLACKLIST_ENABLED'] = True
app.config['JWT_BLACKLIST_TOKEN_CHECKS'] = ['access', 'refresh']


class TokenBlacklist(db.Model):
    """Token黑名单"""
    __tablename__ = 'token_blacklist'
    
    id = Column(Integer, primary_key=True)
    jti = Column(String(120), unique=True, nullable=False)
    token_type = Column(String(10), nullable=False)
    user_id = Column(Integer, nullable=False)
    revoked = Column(Boolean, default=False)
    expires_at = Column(DateTime)


@jwt.token_in_blocklist_loader
def check_if_token_revoked(jwt_header, jwt_payload):
    """检查Token是否被撤销"""
    jti = jwt_payload['jti']
    token = TokenBlacklist.query.filter_by(jti=jti).first()
    return token is not None and token.revoked


# 登录端点
@ns_auth.route('/login')
class Login(Resource):
    @ns_auth.expect(login_model)
    def post(self):
        """用户登录"""
        data = request.get_json()
        
        user = User.query.filter(
            (User.username == data.get('username')) |
            (User.email == data.get('username'))
        ).first()
        
        if not user or not user.check_password(data.get('password')):
            abort(401, message='用户名或密码错误')
        
        # 创建Token
        access_token = create_access_token(identity=user)
        refresh_token = create_refresh_token(identity=user)
        
        response = jsonify({
            'success': True,
            'access_token': access_token,
            'refresh_token': refresh_token,
            'user': marshal(user, user_model)
        })
        
        # 设置Cookie(可选)
        if app.config.get('JWT_TOKEN_LOCATION') and 'cookies' in app.config['JWT_TOKEN_LOCATION']:
            set_access_cookies(response, access_token)
            set_refresh_cookies(response, refresh_token)
        
        return response


@ns_auth.route('/refresh')
class Refresh(Resource):
    @jwt_required(refresh=True)
    def post(self):
        """刷新Token"""
        identity = get_jwt_identity()
        access_token = create_access_token(identity=identity)
        
        return jsonify({
            'success': True,
            'access_token': access_token
        })


@ns_auth.route('/logout')
class Logout(Resource):
    @jwt_required()
    def post(self):
        """登出(撤销Token)"""
        jti = get_jwt()['jti']
        user_id = get_jwt_identity()
        
        # 将Token加入黑名单
        token = TokenBlacklist(
            jti=jti,
            token_type='access',
            user_id=user_id,
            revoked=True
        )
        db.session.add(token)
        db.session.commit()
        
        response = jsonify({'success': True, 'message': '登出成功'})
        unset_jwt_cookies(response)
        
        return response

8.2 权限控制

from functools import wraps

def role_required(*roles):
    """角色检查装饰器"""
    def decorator(fn):
        @wraps(fn)
        @jwt_required()
        def wrapper(*args, **kwargs):
            current_user = get_jwt_identity()
            user = User.query.get(current_user)
            
            if not user or not any(user.has_role(role) for role in roles):
                abort(403, message='权限不足')
            
            return fn(*args, **kwargs)
        return wrapper
    return decorator


def permission_required(*permissions):
    """权限检查装饰器"""
    def decorator(fn):
        @wraps(fn)
        @jwt_required()
        def wrapper(*args, **kwargs):
            current_user = get_jwt_identity()
            user = User.query.get(current_user)
            
            if not user or not any(user.has_permission(p) for p in permissions):
                abort(403, message='权限不足')
            
            return fn(*args, **kwargs)
        return wrapper
    return decorator


def owner_required(get_owner_id):
    """资源所有者检查装饰器"""
    def decorator(fn):
        @wraps(fn)
        @jwt_required()
        def wrapper(*args, **kwargs):
            current_user_id = get_jwt_identity()
            resource_id = kwargs.get('id') or kwargs.get('user_id')
            owner_id = get_owner_id(resource_id)
            
            if current_user_id != owner_id:
                user = User.query.get(current_user_id)
                if not user.has_role('admin'):
                    abort(403, message='无权访问此资源')
            
            return fn(*args, **kwargs)
        return wrapper
    return decorator


# 使用示例
@ns_posts.route('/<int:post_id>')
class PostResource(Resource):
    @jwt_required()
    def get(self, post_id):
        """获取文章(需要登录)"""
        pass
    
    @owner_required(lambda post_id: Post.query.get(post_id).user_id)
    def put(self, post_id):
        """更新文章(需要所有者)"""
        pass
    
    @role_required('admin', 'moderator')
    def delete(self, post_id):
        """删除文章(需要管理员或版主)"""
        pass

九、性能优化

9.1 缓存策略

from flask_caching import Cache

cache = Cache(app, config={'CACHE_TYPE': 'RedisCache'})

class CachedResource:
    """缓存资源"""
    
    @staticmethod
    def cache_key_prefix():
        """缓存键前缀"""
        return f"api:{request.endpoint}"
    
    @staticmethod
    def cache_key_with_params():
        """带参数的缓存键"""
        return f"{CachedResource.cache_key_prefix()}:{request.query_string.decode()}"
    
    @staticmethod
    def cached_response(timeout=300):
        """缓存响应装饰器"""
        def decorator(fn):
            @wraps(fn)
            def wrapper(*args, **kwargs):
                cache_key = CachedResource.cache_key_with_params()
                response = cache.get(cache_key)
                
                if response is None:
                    response = fn(*args, **kwargs)
                    cache.set(cache_key, response, timeout=timeout)
                
                return response
            return wrapper
        return decorator


# 使用示例
class UserListResource(Resource):
    @CachedResource.cached_response(timeout=60)
    @marshal_with(user_list_fields)
    def get(self):
        """获取用户列表(缓存60秒)"""
        pass


# 缓存失效
def invalidate_cache(pattern):
    """使缓存失效"""
    if app.config['CACHE_TYPE'] == 'RedisCache':
        cache.cache._client.delete_pattern(f"*{pattern}*")
    else:
        cache.clear()


class UserResource(Resource):
    def put(self, user_id):
        """更新用户"""
        # ... 更新逻辑
        
        # 使相关缓存失效
        invalidate_cache(f"api:user:{user_id}")
        invalidate_cache("api:users")
        
        return user

9.2 查询优化

from sqlalchemy.orm import lazyload, joinedload, subqueryload

class QueryOptimization:
    """查询优化"""
    
    @staticmethod
    def optimize_user_query():
        """优化用户查询"""
        # 预加载关联数据
        return User.query.options(
            joinedload(User.profile),
            subqueryload(User.posts).joinedload(Post.comments)
        )
    
    @staticmethod
    def paginate_optimized(query, page, per_page):
        """优化分页查询"""
        # 使用延迟加载
        return query.options(lazyload('*')).paginate(
            page=page,
            per_page=per_page,
            error_out=False
        )
    
    @staticmethod
    def count_optimized(model, filters=None):
        """优化计数查询"""
        query = db.session.query(func.count(model.id))
        if filters:
            query = query.filter(*filters)
        return query.scalar()


# 批量操作
class BulkOperations:
    """批量操作"""
    
    @staticmethod
    def bulk_create(model_class, data_list):
        """批量创建"""
        db.session.bulk_insert_mappings(model_class, data_list)
        db.session.commit()
    
    @staticmethod
    def bulk_update(model_class, data_list):
        """批量更新"""
        db.session.bulk_update_mappings(model_class, data_list)
        db.session.commit()
基于Flask+Vue构建个人B站视频知识库:从信息聚合到深度内化 在信息爆炸的时代,高效的知识管理成为个人学习和内容创作的关键。其核心原理在于将分散、非结构化的信息进行系统化聚合、分类与深度加工,从而构建可检索、可关联的个人知识体系。这一过程的技术价值在于,通过自定义的数据模型和检索逻辑,将被动接收转化为主动建构,极大提升了信息利用效率和知识留存率。典型的应用场景包括追踪技术动态、管理学习教程、归档研究资料等,尤其适用于视频这类富媒体内容的管理。本文以B站视频资料管理为例,详细阐述了如何利用Flask后端框架和Vue.js前端技术,实现一个支持视频信息抓取、结构化存储、个 阅读详情

相关推荐

基于Firefox的REST API浏览器自动化:为AI Agent提供真实Web交互环境

在Web自动化与数据采集领域,浏览器自动化技术是实现动态页面交互的核心手段。其原理是通过程序控制浏览器实例,模拟真实用户操作,从而处理JavaScript渲染、表单提交等复杂场景。这项技术的核心价值在于为自动化脚本和AI系统提供了与Web环境交互的标准化接口,广泛应用于数据采集、自动化测试和流程自动化等场景。其中,基于真实浏览器环境的解决方案,如camofox-browser,通过封装Firefox实例为REST API服务,解决了传统方案在语言耦合、资源管理和浏览器指纹真实性方面的痛点。该方案特别适合需要

weixin_30384217的博客 321

Python Flask高级编程之RESTFul API前后端分离(学习笔记)

接下来,我们需要定义 API 的路由和处理函数。在 Flask 中,可以使用@app.route这里的/users表示路由的路径,表示这个路由只支持 GET 方法。get_users函数是这个路由的处理函数,它返回一个 JSON 格式的用户列表。同样的,我们还可以定义其他路由和处理函数,比如获取指定用户信息、添加用户、修改用户信息和删除用户等。

weixin_45897172的博客 2585

大模型流式输出原理与前端实现全解析

流式输出是大语言模型推理过程的自然体现,源于其自回归生成机制——逐token预测、串行依赖、不可并行化。该机制决定了响应必须以增量方式传递,而非整块返回,从而催生了SSE与ReadableStream两类主流传输方案。前者基于HTTP协议扩展,具备自动重连与调试友好特性;后者依托Fetch API原生流能力,支持精细控制与中断操作。技术价值在于保障实时性、降低首字延迟、提升用户感知流畅度,并支撑客服机器人、IDE插件、实时翻译等强交互场景。本文深入剖析从模型推理、后端封装、协议适配到前端渲染的全链路实践。

weixin_34198881的博客 304

探索Flask中的RESTful API设计与实现

​ RESTful API(Representational State Transfer)是一种基于HTTP协议设计的应用程序编程接口(API)风格,用于在客户端和服务器之间进行通信。它提供了一组规范和约束,用于创建可伸缩、可靠和可维护的网络服务。使用有意义的路径来表示资源,例如/users表示用户资源。遵循路径层次结构,使用斜杠分隔不同的资源和子资源,例如表示特定用户的订单资源。避免在路径中使用动词,使用HTTP方法来表示操作类型。url链接中,不能有动词,只能有名词。

ak_bingbing的博客 2185

【WorkBuddy专栏18】WorkBuddy API深度解析——打造开发者友好的AI生态

【WorkBuddy专栏18】WorkBuddy API深度解析——打造开发者友好的AI生态

AI人人懂 1209

基于STM32与ESP32的智能宠物喂食器:从硬件设计到物联网应用开发全流程解析

嵌入式系统与物联网技术是构建智能硬件设备的核心基础。其原理在于通过微控制器(如STM32)实现本地设备的精准感知与控制,再结合无线通信模块(如ESP32)将数据上传至云端,最终通过移动应用实现远程交互。这种技术架构的价值在于打通了物理世界与数字世界的连接,使得传统设备具备了远程监控、智能决策和数据分析的能力。在智能家居、工业自动化、环境监测等场景中,该技术方案被广泛应用。本文聚焦于一个典型的物联网应用——智能宠物喂食器,深入剖析了如何利用STM32实现电机驱动与传感器数据采集,以及如何通过ESP32进行Wi

weixin_33981932的博客 357

基于 Flask 的 RESTful API 实现

写在前面: 本文章只是一个蒟蒻的一个小尝试,存在很多问题,请谨慎观看,欢迎批评指正!

赤城封雪的博客 1106

PCB板质量测试和PCB板

PCB板质量测试和板厂

weixin_51370842的博客 510

Flask 之 RESTful Api

1.Flask-RESTful概述;1.1 Flask-RESTful 提供的最主要的基础就是资源(resources)。资源(Resources)是构建在Flask 可拔插视图之上,只要在你的资源(resource)上定义方法就能够容易地访问多个 HTTP 方法。一个待办事项应用程序的基本的 CRUD 资源看起来像这样:提供的最主要的基础就是资源(resources...

canyunzong5263的博客 461

利用MobaXterm解密Session密码的实战指南

本文提供了利用MobaXterm解密Session密码的详细实战指南,帮助运维工程师和开发者在忘记密码时找回加密存储的远程连接信息。通过Python脚本和注册表操作,逐步解析MobaXterm的AES加密机制,实现密码解密,并分享安全建议和常见问题解决方案。

weixin_26759093的博客 228

Vcenter安全加固-防火墙-只允许特定的IP地址访问Vcenter

3、此时除了被允许的IP地址可以访问之外,其余地址均被防火墙阻挡。2、再进行0.0.0.0/0匹配所有其他IP地址进行拒绝访问。1、只需要关注接受和拒绝即可。2、其余选择不是这个场景使用的。1.只允许特定的IP地址访问Vcenter。1、登录Vcenter管理界面。2、点击防火墙-添加。

博然的宝藏库 7273

Python编码系列—Python RESTful API设计:构建高效网络服务的秘诀

在当今快速发展的互联网技术中,RESTful API已经成为构建网络服务的标准方式。Python以其简洁的语法和强大的框架支持,成为设计RESTful API的热门选择。本文将深入探讨Python RESTful API设计的精髓,结合实际案例,为CSDN社区的读者们揭示高效构建网络服务的秘诀。RESTful API是一种基于HTTP协议的网络服务设计方法,它通过统一的接口简化了客户端和服务器之间的通信。RESTful API设计强调资源的表述性和状态的无状态性,使得API易于理解和使用。

u013889591的专栏 1268

【工控基础】UVW平台踩坑

1 每个轴按顺时针或逆时针走一个距离,就是旋转。 2 图中W动,U轴V轴不动,就是X方向的平移 3 图中U轴V轴同时动,X不动,就是Y方向的平移。

songhuangong123的博客 6925

基于Flask与深度学习的用户行为异常检测系统设计

用户行为分析是网络安全领域的关键技术,通过挖掘操作序列中的模式特征识别潜在威胁。其核心原理是将离散的用户操作转化为时序特征向量,利用深度学习模型学习正常行为模式边界。相比传统规则引擎,基于LSTM或Transformer的检测方案能自动捕获复杂的时间依赖关系,在金融风控和内部威胁检测等场景具有显著优势。本文实现的轻量级系统结合Flask框架与混合神经网络架构,采用注意力机制增强关键行为特征的权重,通过模型量化和预加载优化实现毫秒级实时检测。典型应用包括账号盗用预警、内部违规操作发现等企业安全场景。

weixin_34374684的博客 148

微信对话开放平台智能对话模块的几个亮点

微信AI首席科学家牛成公布了最新开放方案,并重磅宣布全面开放各层次 NLP 能力。具体说来,微信 AI 迈出了“三大步”: 以硬件合作为核心的智言小微硬件开放平台 2.0 正式亮相; 公开以对话开放能力为核心的微信对话开放平台; 全面开放以自然语言处理能力为核心的 NLP 基础技术平台。 参考:微信9年:张小龙指明方向,微信AI全面开放NLP能力 今天公开课上隆重介绍了...

素质云笔记 1万+

常见限流算法和Guava RateLimiter实现

本文探讨了限流(Rate Limiting)的基本原理及常见算法,包括令牌桶算法、漏桶算法和固定窗口算法,并分析了它们在分布式系统中的应用。通过具体示例介绍了Guava的RateLimiter组件的使用,阐述了如何在高并发场景下实现流量控制。限流策略不仅能保障系统稳定性,还能有效防止过载和滥用。掌握合适的限流算法,有助于提升系统的性能和可用性,确保在流量波动时维持服务质量。

曾经“等你生日那天”都遥远得像未来,如今却可欢愉的挥手说“下个十年见” 86万+

flask RESTful api学习 WITH POSTMAN调试_postman如何调用flask api(1)

还有兄弟不知道网络安全面试可以提前刷题吗?费时一周整理的160+网络安全面试题,金九银十,做网络安全面试里的显眼包!王岚嵚工程师面试题(附答案),只能帮兄弟们到这儿了!如果你能答对70%,找一个安全工作,问题不大。对于有1-3年工作经验,想要跳槽的朋友来说,也是很好的温习资料!【完整版领取方式在文末!!内容实在太多,不一一截图了。

2401_84301853的博客 604

【深度学习驱动流体力学】Python流体力学Ansys Fluent

PyFluent 是一个将 Python 编程语言与 Ansys Fluent 流体动力学 (CFD) 仿真软件集成的工具。它允许用户通过 Python 脚本来控制和自动化 Fluent 中的仿真任务,实现从预处理、求解到后处理的全流程控制。PyFluent 结合了 Python 的灵活性和 Fluent 的强大功能,提供了一种高效且灵活的 CFD 仿真方法。PyFluent 是一个强大而灵活的工具,使用户能够通过 Python 脚本全面利用 Ansys Fluent 的功能。

源代码杀手的博客 3120

基于深度学习的AI智能康养训练系统设计与实现

本文针对老龄化社会背景下老年人康复训练需求增长与传统模式效率不足的问题,设计了一套基于深度学习的智能康养训练系统。系统创新性地整合了MediaPipe姿态检测、LSTM动作识别、DTW质量评估和BLE健康监测等技术,通过Flask框架构建Web服务平台。实验表明,该系统动作识别准确率达92.3%,能有效提供实时动作指导和生理监测,显著提升了居家康复训练的科学性和安全性。研究为降低康复服务成本、提高训练效率提供了可行的技术方案,具有重要的社会应用价值。

donoot的博客 332

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

基于Flask的智慧交通大数据可视化大屏毕设设计与实现

智慧交通与大数据可视化是当前计算机毕业设计的高频选题。一个完整的车联网数据平台,需要从数据采集、存储、后端服务到前端展示形成闭环:用MySQL持久化历史数据、Redis缓存实时状态,Flask提供RESTful接口,配合百度地图API与ECharts渲染可视化大屏,再引入机器学习模型进行短时拥堵预测,结合大模型实现自然语言问数。这种架构既满足业务场景的复杂度要求,又具备工程落地的可行性,从模拟数据生成到预警推送,每一步都有清晰的技术选型和踩坑经验,非常适合作为计算机大类学生的实战项目,也能在答辩中充分体现系

weixin_34195364的博客 311

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

M2FP API接口文档详解:POST请求返回JSON与Base64图像

M2FP 全称为,是基于 Transformer 架构的现代语义分割框架。它继承了 Mask2Former 的强大解码器设计,采用“query-based”方式预测每个像素所属的语义类别,同时利用多尺度特征融合提升边界精度。✅与传统 FCN 或 U-Net 的区别- 不再依赖全卷积逐层上采样- 使用注意力机制动态聚焦关键区域- 支持更复杂的标签体系(共 20+ 类身体部位)其骨干网络采用ResNet-101,在保持较高推理速度的同时,增强了对遮挡、姿态变化和多人重叠场景的鲁棒性。

weixin_28931507的博客 645

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

旅拍搭子 TripBuddy - 高德开放平台「智图创想」Skill 创意征集赛参赛作品

《旅拍搭子TripBuddy:智能旅行摄影规划助手》是一款专为旅行摄影爱好者设计的智能工具,通过五大核心功能解决出游痛点:1)景点智能发现引擎,基于用户偏好推荐目的地;2)网红机位数据库,提供热门拍摄点的最佳时间、构图等专业建议;3)时序路线规划,支持松弛/紧凑/摄影三种节奏;4)天气联动功能自动调整行程;5)周边美食住宿推荐。特别适合家庭出游、情侣旅行、周末游客和摄影爱好者,能快速生成个性化方案,兼顾游玩效率与拍照质量。系统集成高德API实现精准地理服务,代码示例展示了景点匹配和路线规划的算法逻辑。

wdracky的专栏 2万+

Flask + XGBoost 水果质量检测系统实战:从模型训练到Web部署

在机器学习应用落地过程中,如何将训练好的模型集成到Web系统,是一个高频刚需。以梯度提升树为代表的集成学习算法,凭借其强大的表格数据拟合能力,成为分类任务的首选模型之一。Flask作为Python生态中轻量灵活的Web框架,为模型推理提供了简洁高效的接口封装。用户通过前端表单提交特征数据,后端调用已部署的模型进行概率预测,并返回类别与置信度,这是典型的机器学习服务化模式。本文以水果质量检测为业务场景,从特征工程、XGBoost模型训练、交叉验证调参,到Flask接口开发、前后端联调,完整演示结构化数据分类系

weixin_33860528的博客 274

网易云音乐无损解析终极指南:打造你的专属音乐库

在数字音乐时代,你是否曾渴望拥有一个属于自己的高品质音乐库?Netease_url项目正是为音乐爱好者量身打造的专业工具,它能够将网易云音乐的丰富资源转化为个人收藏,支持从标准音质到母带级音质的无损解析下载。无论你是追求极致音质的发烧友,还是希望建立个人音乐档案的收藏家,这个开源工具都能满足你的需求。 ## 🎯 项目核心价值:从音乐消费者到音乐管理者 Netease_url不仅仅是一个下载工

gitblog_00786的博客 908

Python编码系列—前端后浪:Python前后端分离开发实战指南

随着Web应用的复杂性日益增加,前后端分离开发模式成为了现代Web开发的主流趋势。这种模式将前端用户界面与后端业务逻辑分离,不仅提升了开发效率,也使得应用更加灵活和可维护。本文将深入探讨Python前后端分离开发的原理、应用场景,并结合实际项目案例,为CSDN社区的读者们展示其在实战中的应用。传统的Web开发中,前端页面和后端代码通常紧密耦合在一起。随着移动互联网的发展和用户对体验的要求提高,前后端分离模式应运而生。它通过API接口实现前后端的交互,使得前端可以独立于后端进行开发和部署。

u013889591的专栏 1543
上一篇: 06-Flask表单处理与数据验证深度解析
下一篇: 07-Flask用户认证会话与邮件管理深度解析
紫丁香
博客等级 码龄14年 6粉丝 119原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值