Skip to content

Routing

routes! macro

The routes! macro is the recommended way to declare routes for new code. It builds an AppWithState<S> (or any builder that exposes .get(), .post(), etc.) from a declarative table.

use rust_web_server::app::App;
use rust_web_server::routes;
use rust_web_server::request::Request;
use rust_web_server::router::PathParams;
use rust_web_server::server::ConnectionInfo;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::core::New;
struct Db;
fn list_users(_req: &Request, _params: &PathParams, _conn: &ConnectionInfo, _db: &Db) -> Response {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
r
}
fn create_user(_req: &Request, _params: &PathParams, _conn: &ConnectionInfo, _db: &Db) -> Response {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n201_created.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n201_created.reason_phrase.to_string();
r
}
let app = routes! {
App::with_state(Db),
GET "/users" => list_users,
POST "/users" => create_user,
};

Syntax:

routes! {
<builder_expression>,
METHOD "pattern" => handler_or_closure,
...
}

Valid methods: GET, POST, PUT, PATCH, DELETE (all caps). A trailing comma after the last route is optional.

Router — direct usage

Use Router directly when you need to compose multiple routers or call handle inside a custom Application::execute.

use rust_web_server::router::{Router, PathParams};
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::range::Range;
use rust_web_server::mime_type::MimeType;
use rust_web_server::core::New;
let router = Router::new()
.get("/users/:id", |_req, params, _conn| {
let id = params.get("id").unwrap_or("unknown");
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
r.content_range_list = vec![Range::get_content_range(
format!("user {}", id).into_bytes(),
MimeType::TEXT_PLAIN.to_string(),
)];
r
})
.delete("/users/:id", |_req, params, _conn| {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n204_no_content.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n204_no_content.reason_phrase.to_string();
r
});
// Inside Application::execute:
// if let Some(response) = router.handle(request, connection) {
// return Ok(response);
// }

router.handle(request, connection) returns Some(Response) on the first match, None if no route matches. Routes are tried in registration order.

Pattern syntax

SyntaxDescriptionExample
/literalExact path segment/users
/:nameNamed segment — captures one segment/users/:id
/*nameWildcard — captures everything after the prefix/files/*path

Rules:

  • A wildcard (*name) must be the last segment in a pattern.
  • The query string is stripped before matching; only the path is used.
  • Patterns are matched segment-by-segment; /users does not match /users/42.
// /users/42/posts/7 → id="42", post_id="7"
router.get("/users/:id/posts/:post_id", handler);
// /files/a/b/c.txt → path="a/b/c.txt"
router.get("/files/*path", handler);

PathParams extraction

Inside a handler, call params.get("name") to retrieve a named segment value. It returns Option<&str>.

.get("/articles/:slug", |_req, params, _conn| {
let slug = params.get("slug").unwrap_or("unknown");
// use slug ...
Response::new()
})

Virtual-host routing

Call .with_host("example.com") before registering routes to restrict a router to requests whose SNI hostname (TLS) or Host header (plain HTTP) matches that value.

let api_router = Router::new()
.with_host("api.example.com")
.get("/status", api_status_handler);
let www_router = Router::new()
.with_host("www.example.com")
.get("/", home_handler);
// In Application::execute:
// api_router.handle(request, connection)
// .or_else(|| www_router.handle(request, connection))

handle() returns None immediately when .with_host() is set and the incoming request’s hostname does not match, so composing multiple host-scoped routers is safe and efficient.