Idempotency-Key 校验错误保留请求上下文
- server-rs/crates/api-server/src/tripo3d/routes.rs:require_idempotency_key 增加 request_context 参数,缺失 / 格式非法的两个 400 改用 into_response_with_context(Some(..)),与同处理器其它校验、定价错误一致;此前传 None,客户端拿不到请求 ID / 关联标识 验证:cargo test -p api-server --locked idempotency_key(相关 6 个用例全绿)
This commit is contained in:
@@ -58,7 +58,7 @@ pub(crate) async fn submit_text_to_model(
|
||||
payload: Result<Json<Model3dTextToModelRequest>, JsonRejection>,
|
||||
) -> Result<(StatusCode, Json<serde_json::Value>), Response> {
|
||||
let payload = parse_json_payload(&request_context, payload)?;
|
||||
let idempotency_key = require_idempotency_key(&headers)?;
|
||||
let idempotency_key = require_idempotency_key(&headers, &request_context)?;
|
||||
let query = validate_text_to_model_request(&payload)
|
||||
.map_err(map_request_error)
|
||||
.map_err(|error| error.into_response_with_context(Some(&request_context)))?;
|
||||
@@ -86,7 +86,7 @@ pub(crate) async fn submit_image_to_model(
|
||||
payload: Result<Json<Model3dImageToModelRequest>, JsonRejection>,
|
||||
) -> Result<(StatusCode, Json<serde_json::Value>), Response> {
|
||||
let payload = parse_json_payload(&request_context, payload)?;
|
||||
let idempotency_key = require_idempotency_key(&headers)?;
|
||||
let idempotency_key = require_idempotency_key(&headers, &request_context)?;
|
||||
let query = validate_image_to_model_request(&payload)
|
||||
.map_err(map_request_error)
|
||||
.map_err(|error| error.into_response_with_context(Some(&request_context)))?;
|
||||
@@ -172,7 +172,10 @@ fn target_source_entity_id(
|
||||
}
|
||||
|
||||
/// 两个提交都必须显式给出 `Idempotency-Key`;缺失或格式非法一律 400,不静默生成键。
|
||||
fn require_idempotency_key(headers: &HeaderMap) -> Result<&str, Response> {
|
||||
fn require_idempotency_key<'a>(
|
||||
headers: &'a HeaderMap,
|
||||
request_context: &RequestContext,
|
||||
) -> Result<&'a str, Response> {
|
||||
let value = headers
|
||||
.get("idempotency-key")
|
||||
.and_then(|value| value.to_str().ok())
|
||||
@@ -184,7 +187,7 @@ fn require_idempotency_key(headers: &HeaderMap) -> Result<&str, Response> {
|
||||
"provider": super::provider::TRIPO_PROVIDER,
|
||||
"message": "3D 生成提交必须携带 Idempotency-Key 请求头。",
|
||||
}))
|
||||
.into_response_with_context(None)
|
||||
.into_response_with_context(Some(request_context))
|
||||
})?;
|
||||
if value.len() > 128 || !value.bytes().all(|byte| (0x21..=0x7e).contains(&byte)) {
|
||||
return Err(AppError::from_status(StatusCode::BAD_REQUEST)
|
||||
@@ -192,7 +195,7 @@ fn require_idempotency_key(headers: &HeaderMap) -> Result<&str, Response> {
|
||||
"provider": super::provider::TRIPO_PROVIDER,
|
||||
"message": "Idempotency-Key 必须是 1-128 个可打印 ASCII 字符,且不能包含空格。",
|
||||
}))
|
||||
.into_response_with_context(None));
|
||||
.into_response_with_context(Some(request_context)));
|
||||
}
|
||||
Ok(value)
|
||||
}
|
||||
@@ -215,23 +218,29 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn idempotency_key_is_required_and_validated() {
|
||||
let request_context = RequestContext::new(
|
||||
"request-idempotency-key".to_string(),
|
||||
"test".to_string(),
|
||||
std::time::Duration::ZERO,
|
||||
false,
|
||||
);
|
||||
let missing = HeaderMap::new();
|
||||
assert!(require_idempotency_key(&missing).is_err());
|
||||
assert!(require_idempotency_key(&missing, &request_context).is_err());
|
||||
|
||||
let mut headers = HeaderMap::new();
|
||||
headers.insert("idempotency-key", HeaderValue::from_static("issue-1"));
|
||||
assert_eq!(
|
||||
require_idempotency_key(&headers).expect("合法键应通过"),
|
||||
require_idempotency_key(&headers, &request_context).expect("合法键应通过"),
|
||||
"issue-1"
|
||||
);
|
||||
|
||||
let mut blank = HeaderMap::new();
|
||||
blank.insert("idempotency-key", HeaderValue::from_static(" "));
|
||||
assert!(require_idempotency_key(&blank).is_err());
|
||||
assert!(require_idempotency_key(&blank, &request_context).is_err());
|
||||
|
||||
let mut spaced = HeaderMap::new();
|
||||
spaced.insert("idempotency-key", HeaderValue::from_static("a b"));
|
||||
assert!(require_idempotency_key(&spaced).is_err());
|
||||
assert!(require_idempotency_key(&spaced, &request_context).is_err());
|
||||
}
|
||||
|
||||
/// 两个路由必须真的挂在应用路由上并走统一鉴权:路径写错或漏 merge 会退化成 404。
|
||||
|
||||
Reference in New Issue
Block a user