修正 AGC 快速编辑 4xx 失败原因取值:优先 details.message 并附 provider 与素材类型上下文
- `editor_api_rejection_reason` 取值顺序改为 `error.details.message` → `details.message` → `error.message` → `error` 文本 → 顶层 `message` - 平台把所有 4xx 兜底成「请求参数不合法」,真实原因写在 `details.message`,原实现只读 `error.message` 导致用户永远看不到原因 - `details` 有值时附带 `provider`,并透出 `assetKind` / `mediaType`,非 BAD_REQUEST 的稳定错误码照旧透出 - 抽出同步函数 `editor_api_rejection_reason_from_body` 并补定向用例,钉住取值顺序、截断与兜底行为
This commit is contained in:
@@ -2459,43 +2459,71 @@ fn is_external_resource_edit_endpoint(endpoint: &str) -> bool {
|
||||
|
||||
/// 从服务端 4xx 响应体里取出**可读的失败原因**,让用户看到的不是笼统的 HTTP 状态码。
|
||||
///
|
||||
/// 平台错误体在不同路由上可能是 `{error:{message}}`、`{error:"文本"}`、`{details:{message}}`
|
||||
/// 或顶层 `{message}`;这里按这个顺序兼容,并截断到 200 字符,避免把整段响应体塞进用户可见文案。
|
||||
/// 平台错误体在不同路由上可能是 `{error:{message,details:{message}}}`、`{error:"文本"}`、
|
||||
/// `{details:{message}}` 或顶层 `{message}`。**`details.message` 优先**:平台把所有 4xx 都
|
||||
/// 兜底成同一条通用文案(`http_error.rs` 的 `resolve_http_error`),真实原因写在
|
||||
/// `details.message` 里(例如「当前素材类型不支持图片快速编辑」),只读 `error.message`
|
||||
/// 会让用户永远看到「请求参数不合法」。
|
||||
async fn editor_api_rejection_reason(response: reqwest::Response) -> Option<String> {
|
||||
let body = response.text().await.ok()?;
|
||||
editor_api_rejection_reason_from_body(body.as_str())
|
||||
}
|
||||
|
||||
fn editor_api_rejection_reason_from_body(body: &str) -> Option<String> {
|
||||
let value: serde_json::Value = serde_json::from_str(body.trim()).ok()?;
|
||||
let candidate = value
|
||||
.get("error")
|
||||
.and_then(|error| {
|
||||
error
|
||||
.get("message")
|
||||
.and_then(|message| message.as_str())
|
||||
.or_else(|| error.as_str())
|
||||
let error = value.get("error");
|
||||
let details = value.get("details");
|
||||
let error_details = details.or_else(|| error.and_then(|error| error.get("details")));
|
||||
// 取值顺序:`error.details.message` → `details.message` → `error.message` → `error` 文本
|
||||
// → 顶层 `message`。前两者才是平台写进去的具体原因,后两者只是兜底。
|
||||
let candidate = error_details
|
||||
.and_then(|error_details| error_details.get("message"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.or_else(|| {
|
||||
details
|
||||
.and_then(|details| details.get("message"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
})
|
||||
.or_else(|| {
|
||||
value
|
||||
.get("details")
|
||||
.and_then(|details| details.get("message"))
|
||||
.and_then(|message| message.as_str())
|
||||
error
|
||||
.and_then(|error| error.get("message"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
})
|
||||
.or_else(|| value.get("message").and_then(|message| message.as_str()))?;
|
||||
let trimmed = candidate.trim();
|
||||
if trimmed.is_empty() {
|
||||
.or_else(|| error.and_then(serde_json::Value::as_str))
|
||||
.or_else(|| value.get("message").and_then(serde_json::Value::as_str))?
|
||||
.trim();
|
||||
if candidate.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// 平台把所有 4xx 都兜底成同一条通用文案(`http_error.rs`),真实原因在 `error.code` 里,
|
||||
// 因此稳定码必须一起透出,否则用户和排障者只能看到"请求参数不合法"。
|
||||
// 平台把所有 4xx 都兜底成同一条通用文案(`http_error.rs`),稳定码和 `details` 里的
|
||||
// provider / assetKind / mediaType 必须一起透出,否则排障者只能看到"请求参数不合法"。
|
||||
let detail_text = |key: &str| {
|
||||
error_details
|
||||
.and_then(|error_details| error_details.get(key))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|detail| !detail.is_empty())
|
||||
};
|
||||
let code = value
|
||||
.get("error")
|
||||
.and_then(|error| error.get("code"))
|
||||
.and_then(|code| code.as_str())
|
||||
.map(str::trim)
|
||||
.filter(|code| !code.is_empty() && *code != "BAD_REQUEST");
|
||||
let reason: String = trimmed.chars().take(200).collect();
|
||||
Some(match code {
|
||||
Some(code) => format!("{reason}|错误码 {code}"),
|
||||
None => reason,
|
||||
})
|
||||
let mut reason: String = candidate.chars().take(200).collect();
|
||||
if let Some(provider) = detail_text("provider") {
|
||||
reason.push_str(format!("|provider {provider}").as_str());
|
||||
}
|
||||
if let Some(asset_kind) = detail_text("assetKind") {
|
||||
reason.push_str(format!("|素材类型 {asset_kind}").as_str());
|
||||
}
|
||||
if let Some(media_type) = detail_text("mediaType") {
|
||||
reason.push_str(format!("|媒体类型 {media_type}").as_str());
|
||||
}
|
||||
if let Some(code) = code {
|
||||
reason.push_str(format!("|错误码 {code}").as_str());
|
||||
}
|
||||
Some(reason)
|
||||
}
|
||||
|
||||
async fn submit_resource_edit_remote(
|
||||
|
||||
Reference in New Issue
Block a user