Serve static data from the binary
For very small (micro) services it can come in handy to just distribute a single binary containing both code and data such as CSS or Javascript and avoid the hassle of dealing with paths, permissions, deployment etc. In this example we use the handy include_dir crate to bundle a directory of data within the compiled binary and the mime_guess crate to guess a MIME type based on the served file.
Dependencies
[dependencies]
axum = "0.5"
include_dir = "0"
mime_guess = "2"
tokio = { version = "1", features = ["full"] }
Code
First of all create a static
directory next to the src
directory and add
this sample CSS file foo.css
:
body {
background-color: #ccc;
}
The interesting part of the code are the route which matches any path prefixed
/static
and the handler. In the handler we first strip the initial slash
(because that would not match with get_file
) and then just try to load the
file. If we have a match, we try to guess a suitable MIME type and return it,
otherwise we just return a 404:
use axum::body::{self, Empty, Full};
use axum::extract::Path;
use axum::http::{header, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::routing::get;
use axum::{Router, Server};
use include_dir::{include_dir, Dir};
static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static");
async fn static_path(Path(path): Path<String>) -> impl IntoResponse {
let path = path.trim_start_matches('/');
let mime_type = mime_guess::from_path(path).first_or_text_plain();
match STATIC_DIR.get_file(path) {
None => Response::builder()
.status(StatusCode::NOT_FOUND)
.body(body::boxed(Empty::new()))
.unwrap(),
Some(file) => Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
HeaderValue::from_str(mime_type.as_ref()).unwrap(),
)
.body(body::boxed(Full::from(file.contents())))
.unwrap(),
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let app = Router::new().route("/static/*path", get(static_path));
Server::bind(&"0.0.0.0:8080".parse()?)
.serve(app.into_make_service())
.await?;
Ok(())
}
Run
Start the server with
cargo run --bin serve-static-from-binary
and it will serve /static/foo.css
as expected
$ curl http://127.0.0.1:8080/static/foo.css
body {
background-color: #ccc;
}