use of deprecated ...: too basic to be useful in real applications
主线位置:不在主线上,在编译期。两处弃用都发生在 0.6.7,到 0.7.1 还在,代码照常能用,只是每次编译都报警告;CI 里开了
-D warnings就是编译失败。bearer/basic的历史和比较方式在 校验与鉴权,TimeoutLayer一族在 超时一族。
flowchart TB w1["ValidateRequestHeaderLayer::bearer / basic"] --> m1["定义在 #![deprecated] 的 require_authorization 模块里"] m1 --> f1["ValidateRequestHeaderLayer::custom,或 AsyncRequireAuthorization"] w2["TimeoutLayer::new"] --> m2["#[deprecated]:默认 408 不再隐含"] m2 --> f2["TimeoutLayer::with_status_code"]
复现
tower-http = { version = "=0.7.1", features = ["timeout", "validate-request", "auth"] }
axum = { version = "=0.8.9", default-features = false }
tokio = { version = "=1.54.1", features = ["macros", "rt", "time"] }
use axum::routing::get;
use axum::Router;
use std::time::Duration;
use tower_http::timeout::TimeoutLayer;
use tower_http::validate_request::ValidateRequestHeaderLayer;
fn main() {
let _app: Router = Router::new()
.route("/", get(|| async { "hi" }))
.layer(ValidateRequestHeaderLayer::bearer("s3cr3t"))
.layer(TimeoutLayer::new(Duration::from_secs(10)));
}
rustc 1.99.0:
warning: use of deprecated associated function `tower_http::auth::require_authorization::<impl tower_http::validate_request::ValidateRequestHeaderLayer<tower_http::auth::require_authorization::Bearer<ResBody>>>::bearer`: too basic to be useful in real applications
--> src/main.rs:10:40
|
10 | .layer(ValidateRequestHeaderLayer::bearer("s3cr3t"))
| ^^^^^^
|
= note: `#[warn(deprecated)]` on by default
warning: use of deprecated associated function `tower_http::timeout::TimeoutLayer::new`: Use `TimeoutLayer::with_status_code` instead
--> src/main.rs:11:26
|
11 | .layer(TimeoutLayer::new(Duration::from_secs(10)));
| ^^^
出处
bearer / basic:CHANGELOG 记作「auth::require_authorization is too basic for real-world」(#591)。auth/require_authorization.rs 第 1 行 是模块级的 #![deprecated(since = "0.6.7", note = "too basic to be useful in real applications")]。bearer 是给 ValidateRequestHeaderLayer 加的 inherent 方法,定义在这个模块里,所以调用它也算用了弃用项。警告里那串很长的路径 require_authorization::<impl ...>::bearer 就是这个原因:方法挂在 validate_request 的类型上,身份却属于 auth::require_authorization。这也解释了为什么只开 validate-request 时根本找不到 bearer,要再开 auth。
CHANGELOG 没有逐条列出「太简陋」在哪,从代码看至少有这几处:令牌拼成 Bearer {token} 后和请求头按字节比较,scheme 大小写敏感,比较不是常数时间,也只能配一个固定令牌。
TimeoutLayer::new:timeout/service.rs 第 29 行 的 #[deprecated(since = "0.6.7", note = "Use TimeoutLayer::with_status_code instead")]。同一个版本加了 with_status_code(#599),CHANGELOG 只写了替代方法和旧的默认值「Previously was StatusCode::REQUEST_TIMEOUT」,没有给理由。可以补一句的是:RFC 9110 里 408 指服务器等客户端把请求发完等得太久,拿它表示「handler 处理超时」本来就不太贴切,现在由调用方自己选。同一个文件里 Timeout::new、Timeout::layer 也一起弃用了。
替代
tower-http = { version = "=0.7.1", features = ["timeout", "validate-request"] }
axum = { version = "=0.8.9", default-features = false }
tower = { version = "=0.5.3", features = ["util"] }
tokio = { version = "=1.54.1", features = ["macros", "rt", "time"] }
use axum::body::Body;
use axum::http::{header, Request, Response, StatusCode};
use axum::routing::get;
use axum::Router;
use std::time::Duration;
use tower::ServiceExt;
use tower_http::timeout::TimeoutLayer;
use tower_http::validate_request::ValidateRequestHeaderLayer;
#[tokio::main(flavor = "current_thread")]
async fn main() {
let expected = "Bearer s3cr3t";
let app: Router = Router::new()
.route("/", get(|| async { "hi" }))
.route("/slow", get(|| async { tokio::time::sleep(Duration::from_secs(1)).await; "late" }))
// 闭包本身就是一个 ValidateRequest:返回 Err(响应) 表示拒绝
.layer(ValidateRequestHeaderLayer::custom(move |req: &mut Request<Body>| {
match req.headers().get(header::AUTHORIZATION) {
Some(v) if v.as_bytes() == expected.as_bytes() => Ok(()),
_ => {
let mut res = Response::new(Body::empty());
*res.status_mut() = StatusCode::UNAUTHORIZED;
Err(res)
}
}
}))
.layer(TimeoutLayer::with_status_code(StatusCode::REQUEST_TIMEOUT, Duration::from_millis(100)));
for (path, auth) in [("/", None), ("/", Some(expected)), ("/slow", Some(expected))] {
let mut req = Request::get(path);
if let Some(a) = auth {
req = req.header(header::AUTHORIZATION, a);
}
let res = app.clone().oneshot(req.body(Body::empty()).unwrap()).await.unwrap();
println!("GET {path:<6} auth={:<5} -> {}", auth.is_some(), res.status());
}
}
编译没有警告,跑 3 次输出相同:
GET / auth=false -> 401 Unauthorized
GET / auth=true -> 200 OK
GET /slow auth=true -> 408 Request Timeout
- 这里的闭包和被弃用的
bearer做的是同一件事,只为说明形状。真要用固定令牌,至少换成常数时间比较;按用户、按数据库查令牌的,用异步的AsyncRequireAuthorization,或者框架自己的提取器(axum 里通常是一个自定义提取器)。 with_status_code想保持旧行为就传StatusCode::REQUEST_TIMEOUT,上面就是;网关后面的服务常用 503 或 504。
小结
bearer/basic定义在整体弃用的require_authorization模块里,调用就报too basic to be useful in real applications。TimeoutLayer::new弃用,换成with_status_code(状态码, 时长),传 408 等于旧行为。- 两条都是 0.6.7 起的警告,0.7.1 没有删掉这些 API。