Skip to main content

Route

Struct Route 

Source
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>

Source

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");
Source

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");
Source

pub fn get<F>(self, f: F) -> Self
where F: Fn(&mut Context<'_, I, E>) -> Result + Send + Sync + 'static,

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);
Source

pub fn post<F>(self, f: F) -> Self
where F: Fn(&mut Context<'_, I, E>) -> Result + Send + Sync + 'static,

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");
Source

pub fn put<F>(self, f: F) -> Self
where F: Fn(&mut Context<'_, I, E>) -> Result + Send + Sync + 'static,

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");
Source

pub fn patch<F>(self, f: F) -> Self
where F: Fn(&mut Context<'_, I, E>) -> Result + Send + Sync + 'static,

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");
Source

pub fn delete<F>(self, f: F) -> Self
where F: Fn(&mut Context<'_, I, E>) -> Result + Send + Sync + 'static,

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");
Source

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);
Source

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);
Source

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"[..]));
Source

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()?;
Source

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");
Source

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()?;
Source

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"[..]));
Source

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"[..]));
Source

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");

Trait Implementations§

Source§

impl<I, R, E: Render> Debug for Route<I, R, E>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<I, R, E = NoRender> !RefUnwindSafe for Route<I, R, E>

§

impl<I, R, E = NoRender> !UnwindSafe for Route<I, R, E>

§

impl<I, R, E> Forget for Route<I, R, E>

§

impl<I, R, E> Freeze for Route<I, R, E>
where Vec<(Method, Arc<dyn Handler<I, E>>)>: Freeze, Access<R>: Freeze,

§

impl<I, R, E> Move for Route<I, R, E>

§

impl<I, R, E> Send for Route<I, R, E>
where Vec<(Method, Arc<dyn Handler<I, E>>)>: Send, Access<R>: Send,

§

impl<I, R, E> Sync for Route<I, R, E>
where Vec<(Method, Arc<dyn Handler<I, E>>)>: Sync, Access<R>: Sync,

§

impl<I, R, E> Unpin for Route<I, R, E>
where Vec<(Method, Arc<dyn Handler<I, E>>)>: Unpin, Access<R>: Unpin,

§

impl<I, R, E> UnsafeUnpin for Route<I, R, E>
where Vec<(Method, Arc<dyn Handler<I, E>>)>: UnsafeUnpin, Access<R>: UnsafeUnpin,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.