pub struct App<R = (), CF = ()> { /* private fields */ }
Expand description
composed application type with router, stateful context and default middlewares.
Implementations§
Source§impl<Obj, CF> App<AppRouter<Obj>, CF>
impl<Obj, CF> App<AppRouter<Obj>, CF>
Sourcepub fn at<F, C, B>(self, path: &'static str, builder: F) -> Selfwhere
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>,
pub fn at<F, C, B>(self, path: &'static str, builder: F) -> Selfwhere
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§impl<R, CF> App<R, CF>
impl<R, CF> App<R, CF>
Sourcepub fn with_state<C>(
self,
state: C,
) -> App<R, Box<dyn Fn() -> Pin<Box<dyn Future<Output = Result<C, Box<dyn Debug>>>>> + Send + Sync>>
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>>
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
}
Sourcepub 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>>
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>>
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>
impl<R, CF> App<R, CF>
Sourcepub fn enclosed<T>(self, transform: T) -> App<EnclosedBuilder<R, T>, CF>
pub fn enclosed<T>(self, transform: T) -> App<EnclosedBuilder<R, T>, CF>
Enclose App with middleware type. Middleware must impl Service trait. See middleware for more.
Sourcepub fn enclosed_fn<Req, T, O>(
self,
transform: T,
) -> App<EnclosedFnBuilder<R, T>, CF>
pub fn enclosed_fn<Req, T, O>( self, transform: T, ) -> App<EnclosedFnBuilder<R, T>, CF>
Enclose App with function as middleware type. See middleware for more.
Source§impl<R, CF> App<R, CF>
impl<R, CF> App<R, CF>
Sourcepub 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,
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.
Sourcepub 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,
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.
Sourcepub 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,
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§
Auto Trait Implementations§
impl<R, CF> Freeze for App<R, CF>
impl<R, CF> RefUnwindSafe for App<R, CF>where
R: RefUnwindSafe,
CF: RefUnwindSafe,
impl<R, CF> Send for App<R, CF>
impl<R, CF> Sync for App<R, CF>
impl<R, CF> Unpin for App<R, CF>
impl<R, CF> UnwindSafe for App<R, CF>where
R: UnwindSafe,
CF: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<S, Arg> ServiceExt<Arg> for Swhere
S: Service<Arg>,
impl<S, Arg> ServiceExt<Arg> for Swhere
S: Service<Arg>,
Source§fn enclosed<T>(self, build: T) -> Pipeline<Self, T, BuildEnclosed>
fn enclosed<T>(self, build: T) -> Pipeline<Self, T, BuildEnclosed>
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>
fn enclosed_fn<T, Req, O>( self, func: T, ) -> Pipeline<Self, AsyncFn<T>, BuildEnclosed>
Source§fn map<F, Res, ResMap>(self, mapper: F) -> Pipeline<Self, Map<F>, BuildEnclosed>
fn map<F, Res, ResMap>(self, mapper: F) -> Pipeline<Self, Map<F>, BuildEnclosed>
<<Self::Response as Service<Req>>::Future as Future>::Output
type with given
closure.