chore: 基线提交(重构前代码快照)
- git 初始化 + .gitignore 补全(根 .env / logs / instance / __pycache__ 等) - 修复 utils/.gitignore 误将整个 utils/ 源码层忽略的问题,补入全部工具源码(含 ip2region.xdb) - 含后端全部代码:application / extensions / utils / config / migrations - 含前端 ui/(后续整体迁走) - 不含根 .env(含密钥,本地保留) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
"""
|
||||
请求解析工具模块
|
||||
=============================================================
|
||||
|
||||
提供请求数据解析相关的工具函数:
|
||||
- get_json_data: 从Flask请求中解析JSON数据
|
||||
- require_json: 装饰器,要求请求必须包含JSON数据
|
||||
- parse_request_body: 解析请求体JSON数据,返回(data, error)元组
|
||||
- parse_id_list: 将逗号分隔的ID字符串解析为整数列表
|
||||
- parse_batch_ids: 从请求体解析批量删除ID数组,校验并转为逗号分隔字符串
|
||||
- like_escape: 转义SQL LIKE通配符,防止用户输入被当作通配符
|
||||
- like_contains: 构建安全的LIKE包含查询值
|
||||
"""
|
||||
|
||||
import logging
|
||||
from functools import wraps
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_json_data(required=True, silent=False):
|
||||
"""
|
||||
获取请求中的JSON数据
|
||||
|
||||
Args:
|
||||
required: 是否必须获取到JSON数据,默认为True
|
||||
silent: 是否静默处理错误,默认为False(False时会检查Content-Type)
|
||||
|
||||
Returns:
|
||||
dict: JSON数据,如果获取失败则返回None或空字典
|
||||
"""
|
||||
from flask import request
|
||||
|
||||
if not silent and not request.is_json:
|
||||
return None
|
||||
|
||||
try:
|
||||
json_data = request.get_json(silent=silent)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
if required and not json_data:
|
||||
return None
|
||||
|
||||
return json_data or {}
|
||||
|
||||
|
||||
def require_json(f):
|
||||
"""
|
||||
装饰器:要求请求必须包含JSON数据
|
||||
|
||||
Examples:
|
||||
@require_json
|
||||
def login():
|
||||
data = get_json_data()
|
||||
# 业务逻辑...
|
||||
"""
|
||||
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
from flask import request
|
||||
|
||||
if not request.is_json:
|
||||
from utils import R
|
||||
return R.failed(msg="参数格式不正确,请使用JSON格式")
|
||||
|
||||
json_data = request.get_json()
|
||||
if not json_data:
|
||||
from utils import R
|
||||
return R.failed(msg="参数不能为空")
|
||||
|
||||
request.parsed_json = json_data
|
||||
return f(*args, **kwargs)
|
||||
|
||||
return decorated_function
|
||||
|
||||
|
||||
def parse_request_body():
|
||||
"""
|
||||
解析请求体JSON数据
|
||||
|
||||
Returns:
|
||||
tuple: (data, error)
|
||||
成功时 data 为解析后的字典,error 为 None
|
||||
失败时 data 为 None,error 为错误描述字符串
|
||||
"""
|
||||
try:
|
||||
data = get_json_data(required=False, silent=True)
|
||||
|
||||
if data is None or data == {}:
|
||||
return None, "参数不能为空"
|
||||
|
||||
return data, None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"解析请求体异常: {str(e)}")
|
||||
return None, "参数处理失败"
|
||||
|
||||
|
||||
def parse_id_list(id_str: str, separator: str = ',') -> list:
|
||||
"""
|
||||
将逗号分隔的ID字符串解析为整数列表
|
||||
|
||||
Args:
|
||||
id_str: 逗号分隔的ID字符串,如 "1,2,3"
|
||||
separator: 分隔符,默认为逗号
|
||||
|
||||
Returns:
|
||||
list: 有效的整数ID列表
|
||||
|
||||
Example:
|
||||
>>> parse_id_list("1,2,3")
|
||||
[1, 2, 3]
|
||||
>>> parse_id_list("1,2,abc,3")
|
||||
[1, 2, 3]
|
||||
"""
|
||||
if not id_str:
|
||||
return []
|
||||
result = []
|
||||
for item in id_str.split(separator):
|
||||
item = item.strip()
|
||||
if item.isdigit():
|
||||
result.append(int(item))
|
||||
return result
|
||||
|
||||
|
||||
def parse_batch_ids():
|
||||
"""
|
||||
从请求体解析批量删除的ID数组,校验并转为逗号分隔字符串
|
||||
|
||||
请求体格式: [1, 2, 3]
|
||||
|
||||
Returns:
|
||||
tuple: (id_str, error_response)
|
||||
成功时 id_str 为 "1,2,3",error_response 为 None
|
||||
失败时 id_str 为 None,error_response 为 R.failed 响应
|
||||
"""
|
||||
from flask import request
|
||||
from utils import R
|
||||
|
||||
ids = request.get_json()
|
||||
if not ids or not isinstance(ids, list) or len(ids) == 0:
|
||||
return None, R.failed("参数错误:ids必须为非空数组")
|
||||
try:
|
||||
id_list = [int(id_val) for id_val in ids]
|
||||
except (ValueError, TypeError):
|
||||
return None, R.failed("参数错误:数组元素必须为数字")
|
||||
|
||||
return ','.join(str(id_val) for id_val in id_list), None
|
||||
|
||||
|
||||
def like_escape(value, escape_char='\\'):
|
||||
"""
|
||||
转义SQL LIKE通配符
|
||||
|
||||
将用户输入中的 %、_ 和转义字符本身进行转义,
|
||||
防止用户输入被当作LIKE通配符导致非预期查询结果。
|
||||
|
||||
Args:
|
||||
value: 原始输入值
|
||||
escape_char: 转义字符,默认为反斜杠
|
||||
|
||||
Returns:
|
||||
str: 转义后的安全字符串
|
||||
|
||||
Example:
|
||||
>>> like_escape('100%')
|
||||
'100\\%'
|
||||
>>> like_escape('test_data')
|
||||
'test\\_data'
|
||||
"""
|
||||
escaped = str(value).replace(escape_char, escape_char * 2)
|
||||
escaped = escaped.replace('%', escape_char + '%')
|
||||
escaped = escaped.replace('_', escape_char + '_')
|
||||
return escaped
|
||||
|
||||
|
||||
def like_contains(value, escape_char='\\'):
|
||||
"""
|
||||
构建安全的LIKE包含查询值(前后加%)
|
||||
|
||||
对用户输入进行通配符转义后,前后添加 % 构建模糊查询值。
|
||||
配合 SQLAlchemy 的 .like() 方法和 escape 参数使用。
|
||||
|
||||
Args:
|
||||
value: 原始输入值
|
||||
escape_char: 转义字符,默认为反斜杠
|
||||
|
||||
Returns:
|
||||
str: 构建好的LIKE查询值,如 '%safe_value%'
|
||||
|
||||
Example:
|
||||
>>> # SQLAlchemy 用法
|
||||
>>> query = query.filter(User.name.like(like_contains(name), escape='\\'))
|
||||
"""
|
||||
return f'%{like_escape(value, escape_char)}%'
|
||||
Reference in New Issue
Block a user