mirror of
https://github.com/tokio-rs/axum.git
synced 2024-12-28 15:30:16 +01:00
Expand static file serving example (#1471)
This commit is contained in:
parent
9c0a89cd09
commit
199a7a66b8
4 changed files with 95 additions and 18 deletions
|
@ -8,6 +8,7 @@ publish = false
|
||||||
axum = { path = "../../axum" }
|
axum = { path = "../../axum" }
|
||||||
axum-extra = { path = "../../axum-extra", features = ["spa"] }
|
axum-extra = { path = "../../axum-extra", features = ["spa"] }
|
||||||
tokio = { version = "1.0", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
|
tower = { version = "0.4", features = ["util"] }
|
||||||
tower-http = { version = "0.3.0", features = ["fs", "trace"] }
|
tower-http = { version = "0.3.0", features = ["fs", "trace"] }
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
1
examples/static-file-server/assets/index.html
Normal file
1
examples/static-file-server/assets/index.html
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Hi from index.html
|
1
examples/static-file-server/assets/script.js
Normal file
1
examples/static-file-server/assets/script.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
console.log("Hello, World!");
|
|
@ -5,13 +5,19 @@
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
http::StatusCode,
|
body::Body,
|
||||||
|
http::{Request, StatusCode},
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::{get, get_service},
|
routing::{get, get_service},
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
|
use axum_extra::routing::SpaRouter;
|
||||||
use std::{io, net::SocketAddr};
|
use std::{io, net::SocketAddr};
|
||||||
use tower_http::{services::ServeDir, trace::TraceLayer};
|
use tower::ServiceExt;
|
||||||
|
use tower_http::{
|
||||||
|
services::{ServeDir, ServeFile},
|
||||||
|
trace::TraceLayer,
|
||||||
|
};
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
|
@ -24,27 +30,95 @@ async fn main() {
|
||||||
.with(tracing_subscriber::fmt::layer())
|
.with(tracing_subscriber::fmt::layer())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
|
tokio::join!(
|
||||||
|
serve(using_spa_router(), 3000),
|
||||||
|
serve(using_serve_dir(), 3001),
|
||||||
|
serve(using_serve_dir_with_assets_fallback(), 3002),
|
||||||
|
serve(using_serve_dir_only_from_root_via_fallback(), 3003),
|
||||||
|
serve(two_serve_dirs(), 3004),
|
||||||
|
serve(calling_serve_dir_from_a_handler(), 3005),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn using_spa_router() -> Router {
|
||||||
// `SpaRouter` is the easiest way to serve assets at a nested route like `/assets`
|
// `SpaRouter` is the easiest way to serve assets at a nested route like `/assets`
|
||||||
// let app = Router::new()
|
//
|
||||||
// .route("/foo", get(|| async { "Hi from /foo" }))
|
// Requests starting with `/assets` will be served from files in the current directory.
|
||||||
// .merge(axum_extra::routing::SpaRouter::new("/assets", "."))
|
// Requests to unknown routes will get `index.html`.
|
||||||
// .layer(TraceLayer::new_for_http());
|
Router::new()
|
||||||
|
|
||||||
// for serving assets directly at the root you can use `tower_http::services::ServeDir`
|
|
||||||
// as the fallback to a `Router`
|
|
||||||
let app: _ = Router::new()
|
|
||||||
.route("/foo", get(|| async { "Hi from /foo" }))
|
.route("/foo", get(|| async { "Hi from /foo" }))
|
||||||
.fallback_service(get_service(ServeDir::new(".")).handle_error(handle_error))
|
.merge(SpaRouter::new("/assets", "assets").index_file("index.html"))
|
||||||
.layer(TraceLayer::new_for_http());
|
}
|
||||||
|
|
||||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
fn using_serve_dir() -> Router {
|
||||||
tracing::debug!("listening on {}", addr);
|
// `SpaRouter` is just a convenient wrapper around `ServeDir`
|
||||||
axum::Server::bind(&addr)
|
//
|
||||||
.serve(app.into_make_service())
|
// You can use `ServeDir` directly to further customize your setup
|
||||||
.await
|
let serve_dir = get_service(ServeDir::new("assets")).handle_error(handle_error);
|
||||||
.unwrap();
|
|
||||||
|
Router::new()
|
||||||
|
.route("/foo", get(|| async { "Hi from /foo" }))
|
||||||
|
.nest_service("/assets", serve_dir.clone())
|
||||||
|
.fallback_service(serve_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn using_serve_dir_with_assets_fallback() -> Router {
|
||||||
|
// for example `ServeDir` allows setting a fallback if an asset is not found
|
||||||
|
// so with this `GET /assets/doesnt-exist.jpg` will return `index.html`
|
||||||
|
// rather than a 404
|
||||||
|
let serve_dir = ServeDir::new("assets").not_found_service(ServeFile::new("assets/index.html"));
|
||||||
|
let serve_dir = get_service(serve_dir).handle_error(handle_error);
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.route("/foo", get(|| async { "Hi from /foo" }))
|
||||||
|
.nest_service("/assets", serve_dir.clone())
|
||||||
|
.fallback_service(serve_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn using_serve_dir_only_from_root_via_fallback() -> Router {
|
||||||
|
// you can also serve the assets directly from the root (not nested under `/assets`)
|
||||||
|
// by only setting a `ServeDir` as the fallback
|
||||||
|
let serve_dir = ServeDir::new("assets").not_found_service(ServeFile::new("assets/index.html"));
|
||||||
|
let serve_dir = get_service(serve_dir).handle_error(handle_error);
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.route("/foo", get(|| async { "Hi from /foo" }))
|
||||||
|
.fallback_service(serve_dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn two_serve_dirs() -> Router {
|
||||||
|
// you can also have two `ServeDir`s nested at different paths
|
||||||
|
let serve_dir_from_assets = get_service(ServeDir::new("assets")).handle_error(handle_error);
|
||||||
|
let serve_dir_from_dist = get_service(ServeDir::new("dist")).handle_error(handle_error);
|
||||||
|
|
||||||
|
Router::new()
|
||||||
|
.nest_service("/assets", serve_dir_from_assets)
|
||||||
|
.nest_service("/dist", serve_dir_from_dist)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::let_and_return)]
|
||||||
|
fn calling_serve_dir_from_a_handler() -> Router {
|
||||||
|
// via `tower::Service::call`, or more conveniently `tower::ServiceExt::oneshot` you can
|
||||||
|
// call `ServeDir` yourself from a handler
|
||||||
|
Router::new().nest_service(
|
||||||
|
"/foo",
|
||||||
|
get(|request: Request<Body>| async {
|
||||||
|
let service = get_service(ServeDir::new("assets")).handle_error(handle_error);
|
||||||
|
let result = service.oneshot(request).await;
|
||||||
|
result
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_error(_err: io::Error) -> impl IntoResponse {
|
async fn handle_error(_err: io::Error) -> impl IntoResponse {
|
||||||
(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong...")
|
(StatusCode::INTERNAL_SERVER_ERROR, "Something went wrong...")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn serve(app: Router, port: u16) {
|
||||||
|
let addr = SocketAddr::from(([127, 0, 0, 1], port));
|
||||||
|
tracing::debug!("listening on {}", addr);
|
||||||
|
axum::Server::bind(&addr)
|
||||||
|
.serve(app.layer(TraceLayer::new_for_http()).into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue