api/apps/__init__.py
导入依赖
导入了多个 Python 库和模块,包括日志记录、系统操作、模块导入、路径处理、Flask 框架、请求处理、CORS(跨源资源共享)支持等。还导入了项目特定的模块,如数据库模型、服务和工具函数。
import logging
import os
import sys
from importlib.util import module_from_spec, spec_from_file_location
from pathlib import Path
from flask import Blueprint, Flask
from werkzeug.wrappers.request import Request
from flask_cors import CORS
from api.db import StatusEnum
from api.db.db_models import close_connection
from api.db.services import UserService
from api.utils import CustomJSONEncoder
from flask_session import Session
from flask_login import LoginManager
from api.settings import SECRET_KEY, stat_logger
from api.settings import API_VERSION, access_logger
from api.utils.api_utils import server_error_response
from itsdangerous.url_safe import URLSafeTimedSerializer as Serializer
__all__ 声明
指定此模块公开的属性,这里只有 app
__all__ = ['app']
日志配置
创建一个名为 'flask.app' 的日志记录器,并将 access_logger 的处理程序添加到其中。
logger = logging.getLogger('flask.app')
for h in access_logger.handlers:
logger.addHandler(h)
修改 Request 对象的 json 属性
将 Flask 的 Request 对象的 json 属性修改为一个属性,该属性会强制且静默地获取 JSON 数据。
Request.json = property(lambda self: self.get_json(force=True, silent=True))
Flask 应用初始化
创建一个 Flask 应用实例 app,并配置 CORS 支持、URL 映射的严格斜杠处理、自定义的 JSON 编码器和异常处理函数。
app = Flask(__name__)
CORS(app, supports_credentials=True,max_age=2592000)
app.url_map.strict_slashes = False
app.json_encoder = CustomJSONEncoder
app.errorhandler(Exception)(server_error_response)
应用配置
设置 Flask 会话的配置,如是否永久保存会话和会话类型。还从环境变量中获取最大内容长度,或默认为 128MB。
## convince for dev and debug
#app.config["LOGIN_DISABLED"] = True
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config['MAX_CONTENT_LENGTH'] = int(os.environ.get("MAX_CONTENT_LENGTH", 128 * 1024 * 1024))
初始化 Flask-Session 和 Flask-Login
初始化 Flask-Session 和 Flask-Login 的 LoginManager。
Session(app)
login_manager = LoginManager()
login_manager.init_app(app)
动态路由注册
定义了两个函数 search_pages_path 和 register_page,用于搜索特定目录中的 *_app.py 文件,并动态加载这些文件作为 Flask 蓝图(Blueprint)注册到应用中。
def search_pages_path(pages_dir):
return [path for path in pages_dir.glob('*_app.py') if not path.name.startswith('.')]
def register_page(page_path):
page_name = page_path.stem.rstrip('_app')
module_name = '.'.join(page_path.parts[page_path.parts.index('api'):-1] + (page_name, ))
spec = spec_from_file_location(module_name, page_path)
page = module_from_spec(spec)
page.app = app
page.manager = Blueprint(page_name, module_name)
sys.modules[module_name] = page
spec.loader.exec_module(page)
page_name = getattr(page, 'page_name', page_name)
url_prefix = f'/{API_VERSION}/{page_name}'
app.register_blueprint(page.manager, url_prefix=url_prefix)
return url_prefix
搜索并注册蓝图
指定搜索蓝图的目录,并使用前面定义的函数搜索并注册这些蓝图。
pages_dir = [
Path(__file__).parent,
Path(__file__).parent.parent / 'api' / 'apps',
]
client_urls_prefix = [
register_page(path)
for dir in pages_dir
for path in search_pages_path(dir)
]
用户加载函数
为 LoginManager 定义一个 request_loader,该函数从请求的头部获取 JWT 令牌,并尝试使用它来加载用户。如果令牌有效且用户状态有效,则返回用户对象;否则返回 None。
@login_manager.request_loader
def load_user(web_request):
jwt = Serializer(secret_key=SECRET_KEY)
authorization = web_request.headers.get("Authorization")
if authorization:
try:
access_token = str(jwt.loads(authorization))
user = UserService.query(access_token=access_token, status=StatusEnum.VALID.value)
if user:
return user[0]
else:
return None
except Exception as e:
stat_logger.exception(e)
return None
else:
return None
请求结束时的数据库连接关闭
使用 Flask 的 teardown_request 装饰器定义一个函数,该函数在每个请求结束时关闭数据库连接。
@app.teardown_request
def _db_close(exc):
close_connection()
2402

被折叠的 条评论
为什么被折叠?



