refactor(middleware): 抽出 ssr_generation/version_headers 中间件到 middleware.rs
main.rs 的 serve() 闭包内有两个内联 async fn 中间件 (ssr_generation_middleware、version_headers_middleware),与已抽出到 middleware.rs 的 add_cache_control/admin_guard 同类却散落在入口,无法独立测试。 将其移入 middleware.rs 作为 pub(crate) 函数,serve() 闭包改用全路径 crate::middleware::xxx 引用(与现有中间件一致),行为不变。 签名归一:原内联用 axum::http::Request<Body>,迁移后改用 axum::extract::Request(与 add_cache_control/admin_guard 一致;二者是同一 类型的别名,from_fn 接受不变)。 挂载点说明(版本头置于最外层合并 router)留在 main.rs 调用处注释,描述的是 挂载决策而非函数本身。
This commit is contained in:
parent
b25abcb9d8
commit
67406f661a
66
src/main.rs
66
src/main.rs
@ -238,64 +238,6 @@ fn main() {
|
|||||||
"版本响应头开关(Server / X-Yggdrasil-Version / X-Yggdrasil-Git)"
|
"版本响应头开关(Server / X-Yggdrasil-Version / X-Yggdrasil-Git)"
|
||||||
);
|
);
|
||||||
|
|
||||||
// SSR 世代号中间件:把当前全局世代号注入请求扩展,并对 GET 请求的
|
|
||||||
// 响应附加 `X-SSR-Generation` 头。这是为未来 Dioxus 支持自定义 SSR 缓存键
|
|
||||||
// 预留的钩子;目前主要提供可观测性,不会实际失效 SSR 缓存。
|
|
||||||
async fn ssr_generation_middleware(
|
|
||||||
req: axum::http::Request<axum::body::Body>,
|
|
||||||
next: axum::middleware::Next,
|
|
||||||
) -> axum::response::Response {
|
|
||||||
let generation = crate::ssr_cache::current_global_generation();
|
|
||||||
let is_get = req.method() == axum::http::Method::GET;
|
|
||||||
let (mut parts, body) = req.into_parts();
|
|
||||||
parts
|
|
||||||
.extensions
|
|
||||||
.insert(crate::ssr_cache::SsrGeneration(generation));
|
|
||||||
let mut response = next.run(axum::http::Request::from_parts(parts, body)).await;
|
|
||||||
if is_get {
|
|
||||||
response.headers_mut().insert(
|
|
||||||
axum::http::header::HeaderName::from_static("x-ssr-generation"),
|
|
||||||
axum::http::HeaderValue::from_str(&generation.to_string())
|
|
||||||
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("0")),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
// 版本头中间件:为所有响应附加 Server / X-Yggdrasil-Version / X-Yggdrasil-Git,
|
|
||||||
// 数据源与启动日志 log_build_info() 同源(crate::build_info::BUILD_INFO)。
|
|
||||||
// 挂在最终合并 router 的最外层(见下方 Ok(router) 前),因此连 /healthz、
|
|
||||||
// /uploads/*、被 CSRF 拒(403)/超时/admin_guard 重定向的响应都会带头,
|
|
||||||
// 探测价值最大。受 EXPOSE_VERSION_HEADERS 控制(默认 true)。
|
|
||||||
async fn version_headers_middleware(
|
|
||||||
req: axum::http::Request<axum::body::Body>,
|
|
||||||
next: axum::middleware::Next,
|
|
||||||
) -> axum::response::Response {
|
|
||||||
let mut response = next.run(req).await;
|
|
||||||
let h = response.headers_mut();
|
|
||||||
// Server 头:产品名/版本,遵循 "Server: product/version" 习惯。
|
|
||||||
h.insert(
|
|
||||||
axum::http::header::SERVER,
|
|
||||||
axum::http::HeaderValue::from_str(&format!(
|
|
||||||
"yggdrasil/{}",
|
|
||||||
crate::build_info::BUILD_INFO.version
|
|
||||||
))
|
|
||||||
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("yggdrasil")),
|
|
||||||
);
|
|
||||||
// X-Yggdrasil-Version:Cargo.toml 版本号。
|
|
||||||
h.insert(
|
|
||||||
axum::http::header::HeaderName::from_static("x-yggdrasil-version"),
|
|
||||||
axum::http::HeaderValue::from_static(crate::build_info::BUILD_INFO.version),
|
|
||||||
);
|
|
||||||
// X-Yggdrasil-Git:git describe(版本+提交数+短hash+脏标记)。
|
|
||||||
h.insert(
|
|
||||||
axum::http::header::HeaderName::from_static("x-yggdrasil-git"),
|
|
||||||
axum::http::HeaderValue::from_str(crate::build_info::BUILD_INFO.git_describe)
|
|
||||||
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("unknown")),
|
|
||||||
);
|
|
||||||
response
|
|
||||||
}
|
|
||||||
|
|
||||||
// 自定义 API 路由:图片上传(大文件,需要更长超时)
|
// 自定义 API 路由:图片上传(大文件,需要更长超时)
|
||||||
// CSRF 校验置于最外层,先拦截非法来源再做超时/限体。
|
// CSRF 校验置于最外层,先拦截非法来源再做超时/限体。
|
||||||
let upload_route = axum::Router::new()
|
let upload_route = axum::Router::new()
|
||||||
@ -348,7 +290,9 @@ fn main() {
|
|||||||
// 合并 Dioxus + CSRF/世代号/缓存头/可选压缩/30s 超时中间件
|
// 合并 Dioxus + CSRF/世代号/缓存头/可选压缩/30s 超时中间件
|
||||||
// layer 顺序:后加的最外层先执行。CSRF 最外层先拦截非法来源。
|
// layer 顺序:后加的最外层先执行。CSRF 最外层先拦截非法来源。
|
||||||
let mut app_routes = dioxus_app
|
let mut app_routes = dioxus_app
|
||||||
.layer(axum::middleware::from_fn(ssr_generation_middleware))
|
.layer(axum::middleware::from_fn(
|
||||||
|
crate::middleware::ssr_generation_middleware,
|
||||||
|
))
|
||||||
.layer(axum::middleware::from_fn(
|
.layer(axum::middleware::from_fn(
|
||||||
crate::middleware::add_cache_control,
|
crate::middleware::add_cache_control,
|
||||||
))
|
))
|
||||||
@ -394,7 +338,9 @@ fn main() {
|
|||||||
// 版本头中间件置于最终合并 router 的最外层:所有端点(含 /healthz、/uploads/*、
|
// 版本头中间件置于最终合并 router 的最外层:所有端点(含 /healthz、/uploads/*、
|
||||||
// 被 CSRF 拒/超时/admin_guard 重定向的响应)都会带上版本头。受 EXPOSE_VERSION_HEADERS 控制。
|
// 被 CSRF 拒/超时/admin_guard 重定向的响应)都会带上版本头。受 EXPOSE_VERSION_HEADERS 控制。
|
||||||
let router = if expose_version_headers {
|
let router = if expose_version_headers {
|
||||||
router.layer(axum::middleware::from_fn(version_headers_middleware))
|
router.layer(axum::middleware::from_fn(
|
||||||
|
crate::middleware::version_headers_middleware,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
router
|
router
|
||||||
};
|
};
|
||||||
|
|||||||
@ -226,6 +226,59 @@ pub(crate) async fn admin_guard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Axum 中间件:把当前 SSR 全局世代号注入请求扩展,并对 GET 请求的响应附加
|
||||||
|
/// `X-SSR-Generation` 头。这是为未来 Dioxus 支持自定义 SSR 缓存键预留的钩子;
|
||||||
|
/// 目前主要提供可观测性,不会实际失效 SSR 缓存。
|
||||||
|
pub(crate) async fn ssr_generation_middleware(
|
||||||
|
req: axum::extract::Request,
|
||||||
|
next: axum::middleware::Next,
|
||||||
|
) -> axum::response::Response {
|
||||||
|
let generation = crate::ssr_cache::current_global_generation();
|
||||||
|
let is_get = req.method() == axum::http::Method::GET;
|
||||||
|
let (mut parts, body) = req.into_parts();
|
||||||
|
parts
|
||||||
|
.extensions
|
||||||
|
.insert(crate::ssr_cache::SsrGeneration(generation));
|
||||||
|
let mut response = next.run(axum::http::Request::from_parts(parts, body)).await;
|
||||||
|
if is_get {
|
||||||
|
response.headers_mut().insert(
|
||||||
|
axum::http::header::HeaderName::from_static("x-ssr-generation"),
|
||||||
|
axum::http::HeaderValue::from_str(&generation.to_string())
|
||||||
|
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("0")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Axum 中间件:为所有响应附加 Server / X-Yggdrasil-Version / X-Yggdrasil-Git 头。
|
||||||
|
/// 数据源与启动日志 `log_build_info()` 同源(`crate::build_info::BUILD_INFO`)。
|
||||||
|
/// 受 `EXPOSE_VERSION_HEADERS` 控制——该开关在 `main.rs` 决定是否挂载本层。
|
||||||
|
pub(crate) async fn version_headers_middleware(
|
||||||
|
req: axum::extract::Request,
|
||||||
|
next: axum::middleware::Next,
|
||||||
|
) -> axum::response::Response {
|
||||||
|
let mut response = next.run(req).await;
|
||||||
|
let h = response.headers_mut();
|
||||||
|
h.insert(
|
||||||
|
axum::http::header::SERVER,
|
||||||
|
axum::http::HeaderValue::from_str(&format!(
|
||||||
|
"yggdrasil/{}",
|
||||||
|
crate::build_info::BUILD_INFO.version
|
||||||
|
))
|
||||||
|
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("yggdrasil")),
|
||||||
|
);
|
||||||
|
h.insert(
|
||||||
|
axum::http::header::HeaderName::from_static("x-yggdrasil-version"),
|
||||||
|
axum::http::HeaderValue::from_static(crate::build_info::BUILD_INFO.version),
|
||||||
|
);
|
||||||
|
h.insert(
|
||||||
|
axum::http::header::HeaderName::from_static("x-yggdrasil-git"),
|
||||||
|
axum::http::HeaderValue::from_str(crate::build_info::BUILD_INFO.git_describe)
|
||||||
|
.unwrap_or_else(|_| axum::http::HeaderValue::from_static("unknown")),
|
||||||
|
);
|
||||||
|
response
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{cache_control_for_path, parse_compression_algorithms, CompressionAlgorithms};
|
use super::{cache_control_for_path, parse_compression_algorithms, CompressionAlgorithms};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user