最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

rust - multiple fallbacks causes them to fail with 500 Internal Error - Stack Overflow

programmeradmin2浏览0评论

I am using Rusts's Axum web framework.

I need to have multiple fallbacks.

First fallback is for delivering static files such as /script.js and /style.css located in my static folder.

Second fallback should be for if any route doesn't match, then I need to deliver the home page (which is also delivered when / is matched). For example for fake routes which render a client side rendered page.

CODE:

dotenv::dotenv().expect("Failed to load environment variables from .env file!");

let state = AppState {
    pool: PgPoolOptions::new().max_connections(70).connect(&std::env::var("DATABASE_URL").expect("DATABASE_URL must be set.")).await.unwrap(),
};

let api_path = "/api";

tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();

let app = axum::Router::new()
    .route("/", get(home_get))
    .route_with_tsr(format!("{api_path}/submit").as_str(), post(submit_post))
    .route_with_tsr(format!("{api_path}/search").as_str(), post(search_post))
    .fallback_service(ServeDir::new(Path::new(&std::env::var("STATIC_FILES").expect("STATIC_FILES must be set."))))
    .fallback(get(home_get))
    .route_layer(axum::middleware::from_fn_with_state(state.clone(),info_middleware))
    .layer(RequestBodyLimitLayer::new(4096))
    .layer(TraceLayer::new_for_http())
    .with_state(state);

let listener = tokio::net::TcpListener::bind(":::8080").await.unwrap();
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await.unwrap();

This isn't working. When going to home page, the static files do not get sent and 500 Internal Error is sent.

Trace logs:

2025-01-19T04:32:37.437061Z DEBUG request{method=GET uri=/ version=HTTP/1.1}: tower_http::trace::on_request: started processing request
2025-01-19T04:32:37.437244Z DEBUG request{method=GET uri=/ version=HTTP/1.1}: tower_http::trace::on_response: finished processing request latency=0 ms status=200
2025-01-19T04:32:37.478539Z DEBUG request{method=GET uri=/style.css?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_request: started processing request
2025-01-19T04:32:37.480165Z DEBUG request{method=GET uri=/style.css?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_response: finished processing request latency=1 ms status=500
2025-01-19T04:32:37.480295Z ERROR request{method=GET uri=/style.css?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_failure: response failed classification=Status code: 500 Internal Server Error latency=1 ms
2025-01-19T04:32:37.482660Z DEBUG request{method=GET uri=/script.js?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_request: started processing request
2025-01-19T04:32:37.487678Z DEBUG request{method=GET uri=/script.js?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_response: finished processing request latency=5 ms status=500
2025-01-19T04:32:37.487877Z ERROR request{method=GET uri=/script.js?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_failure: response failed classification=Status code: 500 Internal Server Error latency=5 ms

If I comment out the second fallback .fallback(get(home_get)), then it starts working.

How to have both fallbacks?

I am using Rusts's Axum web framework.

I need to have multiple fallbacks.

First fallback is for delivering static files such as /script.js and /style.css located in my static folder.

Second fallback should be for if any route doesn't match, then I need to deliver the home page (which is also delivered when / is matched). For example for fake routes which render a client side rendered page.

CODE:

dotenv::dotenv().expect("Failed to load environment variables from .env file!");

let state = AppState {
    pool: PgPoolOptions::new().max_connections(70).connect(&std::env::var("DATABASE_URL").expect("DATABASE_URL must be set.")).await.unwrap(),
};

let api_path = "/api";

tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.init();

let app = axum::Router::new()
    .route("/", get(home_get))
    .route_with_tsr(format!("{api_path}/submit").as_str(), post(submit_post))
    .route_with_tsr(format!("{api_path}/search").as_str(), post(search_post))
    .fallback_service(ServeDir::new(Path::new(&std::env::var("STATIC_FILES").expect("STATIC_FILES must be set."))))
    .fallback(get(home_get))
    .route_layer(axum::middleware::from_fn_with_state(state.clone(),info_middleware))
    .layer(RequestBodyLimitLayer::new(4096))
    .layer(TraceLayer::new_for_http())
    .with_state(state);

let listener = tokio::net::TcpListener::bind(":::8080").await.unwrap();
axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await.unwrap();

This isn't working. When going to home page, the static files do not get sent and 500 Internal Error is sent.

Trace logs:

2025-01-19T04:32:37.437061Z DEBUG request{method=GET uri=/ version=HTTP/1.1}: tower_http::trace::on_request: started processing request
2025-01-19T04:32:37.437244Z DEBUG request{method=GET uri=/ version=HTTP/1.1}: tower_http::trace::on_response: finished processing request latency=0 ms status=200
2025-01-19T04:32:37.478539Z DEBUG request{method=GET uri=/style.css?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_request: started processing request
2025-01-19T04:32:37.480165Z DEBUG request{method=GET uri=/style.css?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_response: finished processing request latency=1 ms status=500
2025-01-19T04:32:37.480295Z ERROR request{method=GET uri=/style.css?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_failure: response failed classification=Status code: 500 Internal Server Error latency=1 ms
2025-01-19T04:32:37.482660Z DEBUG request{method=GET uri=/script.js?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_request: started processing request
2025-01-19T04:32:37.487678Z DEBUG request{method=GET uri=/script.js?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_response: finished processing request latency=5 ms status=500
2025-01-19T04:32:37.487877Z ERROR request{method=GET uri=/script.js?v=1737261157 version=HTTP/1.1}: tower_http::trace::on_failure: response failed classification=Status code: 500 Internal Server Error latency=5 ms

If I comment out the second fallback .fallback(get(home_get)), then it starts working.

How to have both fallbacks?

Share Improve this question edited Jan 19 at 5:53 Ken White 126k15 gold badges235 silver badges463 bronze badges asked Jan 19 at 4:36 sudoExclamationExclamationsudoExclamationExclamation 8,72210 gold badges51 silver badges117 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

A router can only have one fallback route/service. With the .fallback() call in, this handler replaces the fallback service registered with .fallback_service(). The 500 is therefore unrelated to the presence of two fallbacks (since this is impossible); instead, the home_get handler is likely returning this code. You should see identical behavior if you remove the .fallback_service() call completely. This is a separate bug you'll need to fix.

What you probably want to do instead is use ServeDir::fallback to chain the home_get handler as a nested fallback to that service. Note that you need to create a plain tower Service which has no concept of state, so you need to supply the state again for the MethodRouter you'll be using as the fallback service (assuming that this handler even needs access to the state; if it does not then you can skip supplying the state).

For example:

.fallback_service(
    ServeDir::new(...)
    .fallback(get(home_get).with_state(state.clone()))
)
发布评论

评论列表(0)

  1. 暂无评论