fix: expose actionable API error details
This commit is contained in:
+52
-4
@@ -1,17 +1,65 @@
|
||||
import logging
|
||||
|
||||
from rest_framework.views import exception_handler as drf_exception_handler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _error_messages(details):
|
||||
messages = []
|
||||
|
||||
def collect(value):
|
||||
if isinstance(value, dict):
|
||||
for item in value.values():
|
||||
collect(item)
|
||||
elif isinstance(value, (list, tuple)):
|
||||
for item in value:
|
||||
collect(item)
|
||||
elif value is not None:
|
||||
message = str(value).strip()
|
||||
if message and message not in messages:
|
||||
messages.append(message)
|
||||
|
||||
collect(details)
|
||||
return messages
|
||||
|
||||
|
||||
def _error_message(details):
|
||||
messages = _error_messages(details)
|
||||
if not messages:
|
||||
return "请求未能完成"
|
||||
return ";".join(messages)[:300]
|
||||
|
||||
|
||||
def exception_handler(exc, context):
|
||||
response = drf_exception_handler(exc, context)
|
||||
request = context.get("request")
|
||||
if response is None:
|
||||
logger.exception(
|
||||
"api_unhandled_error method=%s path=%s",
|
||||
getattr(request, "method", "-"),
|
||||
getattr(request, "path", "-"),
|
||||
)
|
||||
return response
|
||||
|
||||
request = context.get("request")
|
||||
details = response.data
|
||||
code = getattr(exc, "default_code", "request_error")
|
||||
message = _error_message(details)
|
||||
fields = ",".join(details.keys()) if isinstance(details, dict) else "-"
|
||||
logger.info(
|
||||
"api_request_error method=%s path=%s status=%s code=%s fields=%s message=%s",
|
||||
getattr(request, "method", "-"),
|
||||
getattr(request, "path", "-"),
|
||||
response.status_code,
|
||||
code,
|
||||
fields,
|
||||
message,
|
||||
)
|
||||
response.data = {
|
||||
"error": {
|
||||
"code": getattr(exc, "default_code", "request_error"),
|
||||
"message": "请求未能完成",
|
||||
"details": response.data,
|
||||
"code": code,
|
||||
"message": message,
|
||||
"details": details,
|
||||
"request_id": getattr(request, "request_id", None),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,18 @@ def test_app_js_幂等键兼容非安全上下文():
|
||||
assert source.count('"Idempotency-Key": createIdempotencyKey()') == 2
|
||||
|
||||
|
||||
def test_app_js_api_错误优先显示详情并输出安全诊断日志():
|
||||
source = (STATIC_ROOT / "js" / "app.js").read_text(encoding="utf-8")
|
||||
|
||||
assert "function collectApiErrorMessages(" in source
|
||||
assert "const baseMessage = detailMessages.length" in source
|
||||
assert 'console.error("[Hulumath API]", {' in source
|
||||
assert "requestId: error.requestId" in source
|
||||
assert "error.details = details || null" in source
|
||||
assert "payload.error?.message || payload.detail ||" in source
|
||||
assert "payload.error?.message || payload.detail ||\n (details ?" not in source
|
||||
|
||||
|
||||
def test_app_css_答题提交按钮可见且长弹窗可滚动():
|
||||
source = (STATIC_ROOT / "css" / "app.css").read_text(encoding="utf-8")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user