Files
outlookEmailPlus/outlook_web/middleware/trace.py
T
胡绍康andClaude Opus 4.6 c0562ef721 refactor: 迁移中间件到 middleware 模块
创建 outlook_web/middleware/ 目录,从 legacy.py 迁移:

新增文件:
- middleware/__init__.py - 模块导出
- middleware/trace.py - trace_id 中间件
  - ensure_trace_id(): 为每个请求生成/透传 trace_id
  - attach_trace_id_and_normalize_errors(): 统一写入 X-Trace-Id,标准化错误格式
- middleware/error_handler.py - 错误处理中间件
  - handle_http_exception(): 处理 HTTP 异常
  - handle_exception(): 处理未捕获异常

更新文件:
- app.py: 从 middleware 模块导入,替代 legacy 引用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 11:47:45 +08:00

96 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Trace ID 中间件
功能:
- 为每个请求生成/透传 trace_id
- 在响应中添加 X-Trace-Id 头
- 将 legacy 字符串错误格式标准化为结构化错误
"""
from __future__ import annotations
import json
from typing import Any, Dict
from flask import g, request
from outlook_web.errors import build_error_payload, generate_trace_id
def ensure_trace_id():
"""为每个请求生成/透传 trace_id,便于前后端统一追踪"""
incoming = request.headers.get('X-Trace-Id') or request.headers.get('X-Request-Id')
if incoming:
incoming = incoming.strip()
g.trace_id = incoming[:64]
else:
g.trace_id = generate_trace_id()
def attach_trace_id_and_normalize_errors(response):
"""统一写入 X-Trace-Id,并把 legacy 的字符串错误格式标准化为结构化错误"""
trace_id_value = None
try:
trace_id_value = getattr(g, 'trace_id', None)
if trace_id_value:
response.headers.setdefault('X-Trace-Id', trace_id_value)
except Exception:
trace_id_value = None
try:
if response.is_streamed:
return response
content_type = response.headers.get('Content-Type', '') or ''
if not content_type.startswith('application/json'):
return response
data = response.get_json(silent=True)
if not isinstance(data, dict):
return response
if data.get('success') is not False:
return response
# 统一补齐 trace_id/status 等字段
if isinstance(data.get('error'), dict):
error_obj = dict(data['error'])
mutated = False
if not error_obj.get('trace_id') and trace_id_value:
error_obj['trace_id'] = trace_id_value
mutated = True
if not error_obj.get('status'):
error_obj['status'] = response.status_code if response.status_code >= 400 else 400
mutated = True
if not error_obj.get('code'):
error_obj['code'] = 'UNKNOWN_ERROR'
mutated = True
if not error_obj.get('message'):
error_obj['message'] = '请求失败'
mutated = True
if mutated:
new_data = dict(data)
new_data['error'] = error_obj
response.set_data(json.dumps(new_data, ensure_ascii=False))
return response
# legacy:error 为字符串
if isinstance(data.get('error'), str):
legacy_message = data.get('error') or '请求失败'
status_for_payload = response.status_code if response.status_code >= 400 else 400
error_payload = build_error_payload(
code='LEGACY_ERROR',
message=legacy_message,
err_type='LegacyError',
status=status_for_payload,
details='',
trace_id=trace_id_value
)
new_data = dict(data)
new_data['error'] = error_payload
response.set_data(json.dumps(new_data, ensure_ascii=False))
return response
except Exception:
return response
return response