pub struct RouteBuilder<'r> { /* private fields */ }Expand description
The route-definition DSL handed to the closure in
AppBuilder::routing.
Register handlers with the per-method helpers (get,
post, put,
delete) or the generic method.
Group related routes under a common prefix with route,
which nests cleanly. Each handler may be an extractor closure or anything
implementing Handler.
use churust_core::{Churust, Call, TestClient};
let app = Churust::server()
.routing(|r| {
r.get("/", |_c: Call| async { "home" });
r.route("/api", |r| {
r.get("/health", |_c: Call| async { "ok" });
});
})
.build();
let res = TestClient::new(app).get("/api/health").send().await;
assert_eq!(res.text(), "ok");Implementations§
Source§impl<'r> RouteBuilder<'r>
impl<'r> RouteBuilder<'r>
Sourcepub fn intercept<M: Middleware>(&mut self, mw: M) -> &mut Self
pub fn intercept<M: Middleware>(&mut self, mw: M) -> &mut Self
Apply mw to every route registered in this scope after this call,
including those in nested scopes.
This is v1 design §6’s scope-local interception, and the equivalent of
scope-local interception. Ordering is deliberate: a route registered before
the intercept call is not covered by it, so the reading order of the
block matches the behaviour.
use churust_core::{Call, Churust, Middleware, Next, Response, TestClient};
use async_trait::async_trait;
use http::StatusCode;
struct RequireKey;
#[async_trait]
impl Middleware for RequireKey {
async fn handle(&self, call: Call, next: Next) -> Response {
if call.header("x-key").is_some() {
next.run(call).await
} else {
Response::new(StatusCode::UNAUTHORIZED)
}
}
}
let app = Churust::server()
.routing(|r| {
r.get("/open", |_c: Call| async { "open" });
r.route("/admin", |r| {
r.intercept(RequireKey);
r.get("/panel", |_c: Call| async { "panel" });
});
})
.build();
let client = TestClient::new(app);
assert_eq!(client.get("/open").send().await.status(), StatusCode::OK);
assert_eq!(
client.get("/admin/panel").send().await.status(),
StatusCode::UNAUTHORIZED
);Sourcepub fn method<Marker, H>(
&mut self,
method: Method,
path: &str,
handler: H,
) -> &mut Selfwhere
H: IntoHandler<Marker>,
pub fn method<Marker, H>(
&mut self,
method: Method,
path: &str,
handler: H,
) -> &mut Selfwhere
H: IntoHandler<Marker>,
Register a handler for method at path. Accepts both extractor
closures (via the HandlerFn family) and anything already implementing
Handler — including a pre-boxed BoxHandler — through IntoHandler.
Sourcepub fn guard(&mut self, g: BoxGuard) -> &mut Self
pub fn guard(&mut self, g: BoxGuard) -> &mut Self
Attach a Guard to the route just registered.
Routes may share a (method, path) when guards distinguish them; the
first whose guards all pass serves the request, in registration order,
so an unguarded route registered last is the fallback. A request that
matches no candidate is a 404.
use churust_core::{guard, Call, Churust, TestClient};
let app = Churust::server()
.routing(|r| {
r.get("/", |_c: Call| async { "beta" })
.guard(guard::header("x-beta", "1"));
r.get("/", |_c: Call| async { "stable" });
})
.build();
let client = TestClient::new(app);
assert_eq!(client.get("/").header("x-beta", "1").send().await.text(), "beta");
assert_eq!(client.get("/").send().await.text(), "stable");§Panics
Panics if no route has been registered on this builder yet — there would be nothing for the guard to apply to.
Sourcepub fn max_body_bytes(&mut self, n: usize) -> &mut Self
pub fn max_body_bytes(&mut self, n: usize) -> &mut Self
Cap the request body for the route just registered.
The server-wide max_body_bytes
still bounds what is read off the socket; this tightens it for one
route, which is what lets an upload endpoint be generous while the rest
of the API stays strict. Body extractors (Json<T>, Form<T>) enforce
it and answer 413.
use churust_core::{Call, Churust, Form, TestClient};
use http::StatusCode;
use serde::Deserialize;
#[derive(Deserialize)]
struct Note { text: String }
let app = Churust::server()
.routing(|r| {
r.post("/tiny", |Form(n): Form<Note>| async move { n.text })
.max_body_bytes(8);
})
.build();
let res = TestClient::new(app)
.post("/tiny")
.header("content-type", "application/x-www-form-urlencoded")
.body("text=this-is-far-too-long")
.send()
.await;
assert_eq!(res.status(), StatusCode::PAYLOAD_TOO_LARGE);§Panics
Panics if no route has been registered on this builder yet.
Sourcepub fn get<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
pub fn get<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
Register a GET handler at path. Returns &mut Self for chaining.
Sourcepub fn post<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
pub fn post<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
Register a POST handler at path. Returns &mut Self for chaining.
Sourcepub fn put<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
pub fn put<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
Register a PUT handler at path. Returns &mut Self for chaining.
Sourcepub fn delete<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
pub fn delete<Marker, H>(&mut self, path: &str, handler: H) -> &mut Selfwhere
H: IntoHandler<Marker>,
Register a DELETE handler at path. Returns &mut Self for chaining.
Sourcepub fn route(
&mut self,
path: &str,
f: impl FnOnce(&mut RouteBuilder<'_>),
) -> &mut Self
pub fn route( &mut self, path: &str, f: impl FnOnce(&mut RouteBuilder<'_>), ) -> &mut Self
Open a nested scope: every route registered inside f has path
prepended to its pattern. Scopes nest arbitrarily, so prefixes compose.
Returns &mut Self for chaining.