Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hello plain world!

In the very first example we try to show Hello World!.

The application depends on axum and tokio. The test have some other dependencies.

Run with

cargo run -p example-hello-text

then visit http://localhost:3000/

You will see <h1>Hello, World!</h1>, yes including the HTML tags. That happens as the Content-type of the response was text/plain.

We can see this by using curl in another terminal with the -i flag:

$ curl -i http://localhost:3000
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8
content-length: 22
date: Tue, 15 Apr 2025 09:48:31 GMT

<h1>Hello, World!</h1>

In the next example we'll see how to make axum set the content type to text/html to convince the browser to interpret the HTML.

In the test you can see two ways to check the Content-type. One seems to be more simple, the other one uses headers::ContentType.

Cargo.toml

[package]
name = "example-hello-plain-world"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
axum = { path = "../../axum" }
tokio = { version = "1.0", features = ["full"] }

[dev-dependencies]
headers = "0.4.0"
http-body-util = "0.1.0"
tower = { version = "0.5.2", features = ["util"] }

main.rs

use axum::{routing::get, Router};

#[tokio::main]
async fn main() {
    // build our application with a route
    let app = app();

    // run it
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
        .await
        .unwrap();
    println!("listening on http://{}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}

fn app() -> Router {
    Router::new().route("/", get(handler))
}

async fn handler() -> &'static str {
    "<h1>Hello, World!</h1>"
}

#[cfg(test)]
mod tests;

tests.rs

#![allow(unused)]
fn main() {
use super::*;
use axum::{body::Body, http::Request, http::StatusCode};
use headers::ContentType;
use http_body_util::BodyExt;
use tower::ServiceExt;

#[tokio::test]
async fn test_main_page() {
    let response = app()
        .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
        .await
        .unwrap();

    assert_eq!(response.status(), StatusCode::OK);
    let content_type = response.headers().get("content-type").unwrap();
    assert_eq!(content_type.to_str().unwrap(), "text/plain; charset=utf-8");

    let content_type = response
        .headers()
        .get("content-type")
        .map(|header| header.to_str().unwrap().parse::<ContentType>().unwrap());
    assert_eq!(content_type, Some(ContentType::text_utf8()));

    let body = response.into_body();
    let bytes = body.collect().await.unwrap().to_bytes();
    let html = String::from_utf8(bytes.to_vec()).unwrap();

    assert_eq!(html, "<h1>Hello, World!</h1>");
}
}

Copyright © 2025 • Created with ❤️ by the authors of axum an Gabor Szabo