Struct App

Source
pub struct App<R = (), CF = ()> { /* private fields */ }
Expand description

composed application type with router, stateful context and default middlewares.

Implementations§

Source§

impl App

Source

pub fn new<Obj>() -> App<AppRouter<Obj>>

Construct a new application instance.

Source§

impl<Obj, CF> App<AppRouter<Obj>, CF>

Source

pub fn at<F, C, B>(self, path: &'static str, builder: F) -> Self
where F: RouteGen + Service + Send + Sync, F::Response: for<'r> Service<WebContext<'r, C, B>>, for<'r> WebContext<'r, C, B>: IntoObject<F::Route<F>, (), Object = Obj>,

insert routed service with given string literal as route path to application. services will be routed with following rules:

§Static route

string literal matched against http request’s uri path.

// register string path to handler service.
let app = App::new().at("/users", get(handler_service(handler)));

// handler function extract request's uri path.
async fn handler(PathRef(path): PathRef<'_>) -> StatusCode {
    assert_eq!(path, "/users");
    StatusCode::OK
}

// boilerplate for starting application service in test. in real world this should be achieved
// through App::serve API
let app_service = app.finish().call(()).now_or_panic().unwrap();

// get request with uri can be matched against registered route.
let req = Request::builder().uri("/users").body(Default::default())?;

// execute application service where the request would match handler function
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);

// http query is not included in the path matching.
let req = Request::builder().uri("/users?foo=bar").body(Default::default())?;
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);

// any change on uri path would result in no match of route.
let req = Request::builder().uri("/users/").body(Default::default())?;
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::NOT_FOUND);
§Dynamic route

Along with static routes, the router also supports dynamic route segments. These can either be named or catch-all parameters:

§Named Parameters

Named parameters like /:id match anything until the next / or the end of the path:

// register named param pattern to handler service.
let app = App::new().at("/users/:id", get(handler_service(handler)));

// handler function try to extract a single key/value string pair from url params.
async fn handler(Params(val): Params<u32>) -> StatusCode {
    // the value matches the string literal and it's routing rule registered in App::at.
    assert_eq!(val, 996);
    StatusCode::OK
}

// boilerplate for starting application service in test. in real world this should be achieved
// through App::serve API
let app_service = app.finish().call(()).now_or_panic().unwrap();

// get request with uri can be matched against registered route.
let req = Request::builder().uri("/users/996").body(Default::default())?;

// execute application service where the request would match handler function
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);

// :x pattern only match till next /. and in following request's case it will not find a matching route.
let req = Request::builder().uri("/users/996/fool").body(Default::default())?;
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::NOT_FOUND);
§Catch-all Parameters

Catch-all parameters start with * and match everything after the /. They must always be at the end of the route:

// register named param pattern to handler service.
let app = App::new().at("/*path", get(handler_service(handler)));

// handler function try to extract a single key/value string pair from url params.
async fn handler(Params(path): Params<String>) -> StatusCode {
    assert!(path.ends_with(".css"));
    StatusCode::OK
}

// boilerplate for starting application service in test. in real world this should be achieved
// through App::serve API
let app_service = app.finish().call(()).now_or_panic().unwrap();

// get request with uri can be matched against registered route.
let req = Request::builder().uri("/foo/bar.css").body(Default::default())?;

// execute application service where the request would match handler function
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);

// *x pattern match till the end of uri path. in following request's case it will match against handler
let req = Request::builder().uri("/foo/bar/baz.css").body(Default::default())?;
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);
§Implicit catch-all parameters

Built in http services require catch-all params would implicitly utilize them to reduce user input.

// when nesting App is used as a route service it will silently register it as a catch-call param.
let app = App::new().at("/users", App::new()
    .at("/", get(handler_service(handler)))
    .at("/996", get(handler_service(handler)))
);

// handler function.
async fn handler() -> StatusCode {
    StatusCode::OK
}

// boilerplate for starting application service in test. in real world this should be achieved
// through App::serve API
let app_service = app.finish().call(()).now_or_panic().unwrap();

// get request with uri can be matched against registered route.
let req = Request::builder().uri("/users/").body(Default::default())?;

// execute application service where the request would match handler function
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);

let req = Request::builder().uri("/users/996").body(Default::default())?;
let res = app_service.call(req).now_or_panic()?;
assert_eq!(res.status(), StatusCode::OK);
§Routing Priority

Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority:

let app = App::new()
    .at("/", Redirect::see_other("/index.html"))        // high priority
    .at("/index.html", Html("<h1>Hello,World!</h1>"))   // high priority
    .at("/*path", get(handler_service(handler)))        // low priority
    .serve();

async fn handler() -> &'static str {
    "todo"
}
Source

pub fn at_typed<T, C>(self, typed: T) -> Self
where T: TypedRoute<C, Route = Obj>,

insert typed route service with given path to application.

Source§

impl<R, CF> App<R, CF>

Source

pub fn with_state<C>( self, state: C, ) -> App<R, Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<C, Box<dyn Debug>>>>> + Send + Sync>>
where C: Send + Sync + Clone + 'static,

Construct App with a thread safe state that will be shared among all tasks and worker threads.

State accessing is based on generic type approach where the State type and it’s typed fields are generally opaque to middleware and routing services of the application. In order to cast concrete type from generic state type std::borrow::Borrow trait is utilized. See example below for explanation.

§Example
// our typed state.
#[derive(Clone, Default)]
struct State {
    string: String,
    usize: usize
}

// implement Borrow trait to enable borrowing &String type from State.
impl BorrowState<String> for State {
    fn borrow(&self) -> &String {
        &self.string
    }
}

App::new()
    .with_state(State::default())// construct app with state type.
    .at("/", handler_service(index)) // a function service that have access to state.
    .enclosed_fn(middleware_fn); // a function middleware that have access to state

// the function service don't know what the real type of application state is.
// it only needs to know &String can be borrowed from it.
async fn index(_: StateRef<'_, String>) -> &'static str {
    ""
}

// similar to function service. the middleware does not need to know the real type of C.
// it only needs to know it implement according trait.
async fn middleware_fn<S, C, Res>(service: &S, ctx: WebContext<'_, C>) -> Result<Res, Error>
where
    S: for<'r> Service<WebContext<'r, C>, Response = Res, Error = Error>,
    C: BorrowState<String> // annotate we want to borrow &String from generic C state type.
{
    // WebContext::state would return &C then we can call Borrow::borrow on it to get &String
    let _string = ctx.state().borrow();
    // or use extractor manually like in function service.
    let _string = StateRef::<'_, String>::from_request(&ctx).await?;
    service.call(ctx).await
}
Source

pub fn with_async_state<CF1, Fut, C, E>( self, builder: CF1, ) -> App<R, Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<C, Box<dyn Debug>>>>> + Send + Sync>>
where CF1: Fn() -> Fut + Send + Sync + 'static, Fut: Future<Output = Result<C, E>> + 'static, E: Debug + 'static,

Construct App with async closure which it’s output would be used as state. async state is used to produce thread per core and/or non thread safe state copies. The output state is not bound to Send and Sync auto traits.

Source§

impl<R, CF> App<R, CF>
where R: Service + Send + Sync, R::Error: Debug + 'static,

Source

pub fn enclosed<T>(self, transform: T) -> App<EnclosedBuilder<R, T>, CF>
where T: Service<Result<R::Response, R::Error>>,

Enclose App with middleware type. Middleware must impl Service trait. See middleware for more.

Source

pub fn enclosed_fn<Req, T, O>( self, transform: T, ) -> App<EnclosedFnBuilder<R, T>, CF>
where T: AsyncFn(&R::Response, Req) -> O + Clone,

Enclose App with function as middleware type. See middleware for more.

Source

pub fn map<T, Res, ResMap>(self, mapper: T) -> App<MapBuilder<R, T>, CF>
where T: Fn(Res) -> ResMap + Clone, Self: Sized,

Mutate <<Self::Response as Service<Req>>::Future as Future>::Output type with given closure.

Source§

impl<R, CF> App<R, CF>
where R: Service + Send + Sync, R::Error: Debug + 'static,

Source

pub fn finish<C, ResB, SE>( self, ) -> impl Service<Response = impl ReadyService + Service<WebRequest, Response = WebResponse<Either<ResB, ResponseBody>>, Error = Infallible>, Error = impl Debug>
where R::Response: ReadyService + for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE>, SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible>, CF: IntoCtx<Ctx = C>, C: 'static,

Finish App build. No other App method can be called afterwards.

Source

pub fn finish_boxed<C, ResB, SE, BE>( self, ) -> AppObject<impl ReadyService + Service<WebRequest, Response = WebResponse, Error = Infallible>>
where R: 'static, R::Response: ReadyService + for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE> + 'static, SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible> + 'static, ResB: Stream<Item = Result<Bytes, BE>> + 'static, BE: Error + Send + Sync + 'static, CF: IntoCtx<Ctx = C> + 'static, C: 'static,

Finish App build. No other App method can be called afterwards.

Source

pub fn serve<C, ResB, SE>( self, ) -> HttpServer<impl Service<Response = impl ReadyService + Service<WebRequest, Response = WebResponse<Either<ResB, ResponseBody>>, Error = Infallible>, Error = impl Debug>>
where R: 'static, R::Response: ReadyService + for<'r> Service<WebContext<'r, C>, Response = WebResponse<ResB>, Error = SE>, SE: for<'r> Service<WebContext<'r, C>, Response = WebResponse, Error = Infallible> + 'static, ResB: 'static, CF: IntoCtx<Ctx = C> + 'static, C: 'static,

Finish App build and serve is with HttpServer. No other App method can be called afterwards.

Trait Implementations§

Source§

impl<R, F> PathGen for App<R, F>
where R: PathGen,

Source§

fn path_gen(&mut self, prefix: &str) -> String

path generator. Read more
Source§

impl<R, F> RouteGen for App<R, F>
where R: RouteGen,

Source§

type Route<R1> = <R as RouteGen>::Route<R1>

service builder type for generating the final route service.
Source§

fn route_gen<R1>(route: R1) -> Self::Route<R1>

route service generator. Read more
Source§

impl<R, Arg> Service<Arg> for App<R>
where R: Service<Arg>,

Source§

type Response = <R as Service<Arg>>::Response

The Ok part of output future.
Source§

type Error = <R as Service<Arg>>::Error

The Err part of output future.
Source§

async fn call(&self, req: Arg) -> Result<Self::Response, Self::Error>

Source§

impl<R, Arg, C> Service<Arg> for App<R, Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<C, Box<dyn Debug>>>>> + Send + Sync>>
where R: Service<Arg>,

Source§

type Response = NestAppService<C, <R as Service<Arg>>::Response>

The Ok part of output future.
Source§

type Error = <R as Service<Arg>>::Error

The Err part of output future.
Source§

async fn call(&self, req: Arg) -> Result<Self::Response, Self::Error>

Auto Trait Implementations§

§

impl<R, CF> Freeze for App<R, CF>
where R: Freeze, CF: Freeze,

§

impl<R, CF> RefUnwindSafe for App<R, CF>

§

impl<R, CF> Send for App<R, CF>
where R: Send, CF: Send,

§

impl<R, CF> Sync for App<R, CF>
where R: Sync, CF: Sync,

§

impl<R, CF> Unpin for App<R, CF>
where R: Unpin, CF: Unpin,

§

impl<R, CF> UnwindSafe for App<R, CF>
where R: UnwindSafe, CF: UnwindSafe,

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> BorrowState<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<S, Arg> ServiceExt<Arg> for S
where S: Service<Arg>,

Source§

fn enclosed<T>(self, build: T) -> Pipeline<Self, T, BuildEnclosed>
where T: Service<Result<Self::Response, Self::Error>>, Self: Sized,

Enclose Self with given T as Service<<Self as Service<_>>::Response>>. In other word T would take Self’s Service::Response type as it’s generic argument of Service<_> impl.
Source§

fn enclosed_fn<T, Req, O>( self, func: T, ) -> Pipeline<Self, AsyncFn<T>, BuildEnclosed>
where T: AsyncFn(&Self::Response, Req) -> O + Clone, Self: Sized,

Function version of Self::enclosed method.
Source§

fn map<F, Res, ResMap>(self, mapper: F) -> Pipeline<Self, Map<F>, BuildEnclosed>
where F: Fn(Res) -> ResMap + Clone, Self: Sized,

Mutate <<Self::Response as Service<Req>>::Future as Future>::Output type with given closure.
Source§

fn map_err<F, Err, ErrMap>( self, err: F, ) -> Pipeline<Self, MapErr<F>, BuildEnclosed>
where F: Fn(Err) -> ErrMap + Clone, Self: Sized,

Mutate <Self::Response as Service<Req>>::Error type with given closure.
Source§

fn and_then<F>(self, factory: F) -> Pipeline<Self, F, BuildAndThen>
where F: Service<Arg>, Self: Sized,

Chain another service factory who’s service takes Self’s Service::Response output as Service::Request.
Source§

impl<S, Req> ServiceObject<Req> for S
where S: Service<Req>,

Source§

type Response = <S as Service<Req>>::Response

Source§

type Error = <S as Service<Req>>::Error

Source§

fn call<'s>( &'s self, req: Req, ) -> Pin<Box<dyn Future<Output = Result<<S as ServiceObject<Req>>::Response, <S as ServiceObject<Req>>::Error>> + 's>>
where Req: 's,

Source§

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

Source§

type Error = Infallible

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

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

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

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more