use axum::{extract::Request, http::header::CONTENT_TYPE, middleware::Next, response::Response}; use tracing::{error, warn}; use crate::{ http_error::AppError, request_context::{RequestContext, resolve_request_id}, }; pub async fn normalize_error_response(request: Request, next: Next) -> Response { let request_id = resolve_request_id(&request); let request_context = request.extensions().get::().cloned(); let response = next.run(request).await; if !should_normalize_error_response(&response) { return response; } let app_error = AppError::from_status(response.status()); let status = response.status(); let request_id = request_id.as_deref().unwrap_or("unknown"); if status.is_server_error() { error!( %request_id, status = status.as_u16(), error_code = app_error.code(), "request failed" ); } else { warn!( %request_id, status = status.as_u16(), error_code = app_error.code(), "request failed" ); } app_error.into_response_with_context(request_context.as_ref()) } fn should_normalize_error_response(response: &Response) -> bool { let status = response.status(); if !(status.is_client_error() || status.is_server_error()) { return false; } // 当前阶段只兜底框架默认的空错误响应或非 JSON 错误响应,避免覆盖后续业务 handler 主动构造的错误 body。 !response.headers().contains_key(CONTENT_TYPE) }