diff --git a/examples/handle-head-request/Cargo.toml b/examples/handle-head-request/Cargo.toml new file mode 100644 index 00000000..0672b238 --- /dev/null +++ b/examples/handle-head-request/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "example-handle-head-request" +version = "0.1.0" +edition = "2021" +publish = false + +[dependencies] +axum = { path = "../../axum" } +tokio = { version = "1.0", features = ["full"] } + +[dev-dependencies] +hyper = { version = "0.14", features = ["full"] } +tower = { version = "0.4", features = ["util"] } diff --git a/examples/handle-head-request/src/main.rs b/examples/handle-head-request/src/main.rs new file mode 100644 index 00000000..492d3425 --- /dev/null +++ b/examples/handle-head-request/src/main.rs @@ -0,0 +1,82 @@ +//! Run with +//! +//! ```not_rust +//! cd examples && cargo run -p example-handle-head-request +//! ``` + +use axum::response::{IntoResponse, Response}; +use axum::{http, routing::get, Router}; +use std::net::SocketAddr; + +fn app() -> Router { + Router::new().route("/get-head", get(get_head_handler)) +} + +#[tokio::main] +async fn main() { + let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); + + axum::Server::bind(&addr) + .serve(app().into_make_service()) + .await + .unwrap(); +} + +// GET routes will also be called for HEAD requests but will have the response body removed. +// You can handle the HEAD method explicitly by extracting `http::Method` from the request. +async fn get_head_handler(method: http::Method) -> Response { + // it usually only makes sense to special-case HEAD + // if computing the body has some relevant cost + if method == http::Method::HEAD { + return ([("x-some-header", "header from HEAD")]).into_response(); + } + + // then do some computing task in GET + do_some_computing_task(); + + ([("x-some-header", "header from GET")], "body from GET").into_response() +} + +fn do_some_computing_task() { + // TODO +} + +#[cfg(test)] +mod tests { + use super::*; + use axum::body::Body; + use axum::http::{Request, StatusCode}; + use tower::ServiceExt; + + #[tokio::test] + async fn test_get() { + let app = app(); + + let response = app + .oneshot(Request::get("/get-head").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.headers()["x-some-header"], "header from GET"); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + assert_eq!(&body[..], b"body from GET"); + } + + #[tokio::test] + async fn test_implicit_head() { + let app = app(); + + let response = app + .oneshot(Request::head("/get-head").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + assert_eq!(response.headers()["x-some-header"], "header from HEAD"); + + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + assert!(body.is_empty()); + } +}