pub struct Route<I, R, E: Render = NoRender> { /* private fields */ }Expand description
A route: a path pattern, one handler per method, and who may use it.
Patterns are made of literal segments, parameters (:id, one segment)
and one wildcard at the end (*rest, the rest of the path). Literals are
written decoded (/café); web-net compares them with the canonical
request path.
use web_net::{App, get, Route};
let app = App::builder()
.route(get("/", |cx| cx.html(200, "<h1>Home</h1>")))
.route(
Route::at("/contact")
.get(|cx| cx.html(200, "<form method=post></form>"))
.post(|cx| cx.redirect(303, "/contact/thanks"))
// A real form gets CSRF protection: Builder::csrf and a gate
// whose identities carry a binding.
.csrf_exempt("example: the POST changes nothing"),
)
.build()?;A closure that reads fields of a custom identity types its parameter,
because the closure is checked before the builder fixes the type (a
named fn handler needs nothing extra):
use web_net::{Context, Route, get};
struct User { name: String }
impl web_net::Identity for User {
fn is_guest(&self) -> bool { self.name.is_empty() }
}
enum Rule { SignedIn }
let account: Route<User, Rule> = get("/account", |cx: &mut Context<'_, User>| {
let name = cx.identity().name.clone();
cx.text(200, &name)
})
.allow(Rule::SignedIn);Implementations§
Source§impl<I: 'static, R, E: Render> Route<I, R, E>
impl<I: 'static, R, E: Render> Route<I, R, E>
Sourcepub fn at(pattern: impl Into<String>) -> Self
pub fn at(pattern: impl Into<String>) -> Self
A route at pattern with no methods yet.
use web_net::{App, Route};
let app = App::builder()
.route(Route::at("/about").get(|cx| cx.text(200, "about")))
.build()?;
let r = serve_net::testing::respond(&app, b"GET /about HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.text(), "about");Sourcepub fn on(self, method: Method, handler: impl Handler<I, E>) -> Self
pub fn on(self, method: Method, handler: impl Handler<I, E>) -> Self
Adds a handler for method. HEAD follows GET unless it has its
own; OPTIONS is answered for every route unless it has its own.
use serve_net::Method;
use web_net::{App, Context, Guest, Route};
fn store(cx: &mut Context<'_, Guest>) -> web_net::Result {
cx.text(200, "stored")
}
let app = App::builder()
.route(Route::at("/items").on(Method::Put, store).csrf_exempt("example: no browser form"))
.build()?;
let r = serve_net::testing::respond(
&app,
b"PUT /items HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n",
);
assert_eq!(r.text(), "stored");Sourcepub fn get<F>(self, f: F) -> Self
pub fn get<F>(self, f: F) -> Self
Adds a GET handler.
use web_net::{App, Route};
let app = App::builder()
.route(Route::at("/").get(|cx| cx.text(200, "home")))
.build()?;
// HEAD follows GET.
let r = serve_net::testing::respond(&app, b"HEAD / HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.status, 200);Sourcepub fn post<F>(self, f: F) -> Self
pub fn post<F>(self, f: F) -> Self
Adds a POST handler.
use web_net::{App, Route};
let app = App::builder()
.route(Route::at("/items/:id").post(|cx| cx.text(200, "created")).csrf_exempt("example: no browser form"))
.build()?;
let r = serve_net::testing::respond(
&app,
b"POST /items/1 HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n",
);
assert_eq!(r.text(), "created");Sourcepub fn put<F>(self, f: F) -> Self
pub fn put<F>(self, f: F) -> Self
Adds a PUT handler.
use web_net::{App, Route};
let app = App::builder()
.route(Route::at("/items/:id").put(|cx| cx.text(200, "replaced")).csrf_exempt("example: no browser form"))
.build()?;
let r = serve_net::testing::respond(
&app,
b"PUT /items/1 HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n",
);
assert_eq!(r.text(), "replaced");Sourcepub fn patch<F>(self, f: F) -> Self
pub fn patch<F>(self, f: F) -> Self
Adds a PATCH handler.
use web_net::{App, Route};
let app = App::builder()
.route(Route::at("/items/:id").patch(|cx| cx.text(200, "changed")).csrf_exempt("example: no browser form"))
.build()?;
let r = serve_net::testing::respond(
&app,
b"PATCH /items/1 HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n",
);
assert_eq!(r.text(), "changed");Sourcepub fn delete<F>(self, f: F) -> Self
pub fn delete<F>(self, f: F) -> Self
Adds a DELETE handler.
use web_net::{App, Route};
let app = App::builder()
.route(Route::at("/items/:id").delete(|cx| cx.text(200, "deleted")).csrf_exempt("example: no browser form"))
.build()?;
let r = serve_net::testing::respond(
&app,
b"DELETE /items/1 HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n",
);
assert_eq!(r.text(), "deleted");Sourcepub fn public(self) -> Self
pub fn public(self) -> Self
Anyone may use the route, guests included. With a gate that has
rules, public is a declaration, never a default: a route with neither
public nor allow stops the build. With Open,
whose rule type has no values, every route is public and needs
neither.
use web_net::{App, get};
// `NoStaff` is a gate that denies `Rule::Staff` to everyone.
let app = App::with_gate(NoStaff)
.route(get("/", |cx| cx.text(200, "home")).public())
.build()?;
let r = serve_net::testing::respond(&app, b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.status, 200);Sourcepub fn allow(self, rule: R) -> Self
pub fn allow(self, rule: R) -> Self
Those rule allows may use the route; the gate’s authorize decides
on every request. Inside a group with a rule, both rules hold: the
group’s first, then this one.
use web_net::{App, get};
// `NoStaff` is a gate that denies `Rule::Staff` to everyone.
let app = App::with_gate(NoStaff)
.route(get("/admin", |cx| cx.text(200, "admin")).allow(Rule::Staff))
.build()?;
let r = serve_net::testing::respond(&app, b"GET /admin HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.status, 403);Sourcepub fn name(self, name: &'static str) -> Self
pub fn name(self, name: &'static str) -> Self
A name for logs, events, and hooks.
use web_net::{App, Hook, Routes, get};
let app = App::builder()
.hook(Hook::Finish, Routes::named(&["home"]), |cx| {
cx.set_header("x-page", "home");
Ok(())
})
.route(get("/", |cx| cx.text(200, "home")).name("home"))
.build()?;
let r = serve_net::testing::respond(&app, b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.header("x-page"), Some(&b"home"[..]));Sourcepub fn work(self, work: WorkClass) -> Self
pub fn work(self, work: WorkClass) -> Self
How the handlers behave: Quick (the default), Io, or Cpu. A
dispatcher in front of web-net reads it through
App::classify to move slow work off the
connection’s worker.
use serve_net::WorkClass;
use web_net::{App, get};
// The report waits on a database: a dispatcher can run it off the
// connection's worker.
let app = App::builder()
.route(get("/report", |cx| cx.text(200, "report")).work(WorkClass::Io))
.build()?;Sourcepub fn csrf_exempt(self, reason: &'static str) -> Self
pub fn csrf_exempt(self, reason: &'static str) -> Self
Takes the route out of CSRF protection, with the reason (a webhook signed by its sender, an API authenticated by a bearer token that a browser never sends on its own). The reason is required and must not be empty: an exemption is a decision, never a default.
use web_net::{App, post};
let app = App::builder()
.route(post("/hooks/build", |cx| cx.text(200, "queued")).csrf_exempt("signed by the CI server"))
.build()?;
let r = serve_net::testing::respond(
&app,
b"POST /hooks/build HTTP/1.1\r\nHost: localhost\r\nContent-Length: 0\r\n\r\n",
);
assert_eq!(r.text(), "queued");Sourcepub fn form_limits(self, limits: Limits) -> Self
pub fn form_limits(self, limits: Limits) -> Self
The budgets for reading this route’s forms (field count, sizes, files;
default form_net::Limits::DEFAULT). Keep them to what the form
needs: they bound what one request can make the server hold.
use web_net::{App, form_net, post};
let limits = form_net::Limits::DEFAULT.with_max_fields(4);
let app = App::builder()
.route(
post("/search", |cx| cx.text(200, "results"))
.form_limits(limits)
.csrf_exempt("example: changes nothing"),
)
.build()?;Sourcepub fn cors(self, cors: Cors) -> Self
pub fn cors(self, cors: Cors) -> Self
The route’s CORS policy: preflights answered, replies to allowed origins readable by their scripts.
use web_net::{App, Cors, get};
let app = App::builder()
.route(
get("/api/items", |cx| cx.bytes(200, "application/json", b"[]"))
.cors(Cors::new().origin("https://app.example.com")),
)
.build()?;
let r = serve_net::testing::respond(
&app,
b"GET /api/items HTTP/1.1\r\nHost: localhost\r\nOrigin: https://app.example.com\r\n\r\n",
);
assert_eq!(r.header("access-control-allow-origin"), Some(&b"https://app.example.com"[..]));Sourcepub fn cache(self, policy: CachePolicy) -> Self
pub fn cache(self, policy: CachePolicy) -> Self
How the route’s pages may be cached.
use web_net::{App, CachePolicy, get};
let app = App::builder()
.route(get("/account", |cx| cx.text(200, "yours")).cache(CachePolicy::NoStore))
.build()?;
let r = serve_net::testing::respond(&app, b"GET /account HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.header("cache-control"), Some(&b"no-store"[..]));Sourcepub fn template(self, name: &'static str) -> Self
pub fn template(self, name: &'static str) -> Self
The template Context::render uses. Resolved when the
application is built: an unknown name stops the build.
use web_net::{App, Context, Guest, get};
// `Pages` is an engine whose one template, "page", writes the view (a `str`).
let app = App::builder()
.render(Pages)
.route(get("/", |cx: &mut Context<'_, Guest, Pages>| cx.render("Home")).template("page"))
.build()?;
let r = serve_net::testing::respond(&app, b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n");
assert_eq!(r.text(), "Home");