Skip to main content

AppWithState

Struct AppWithState 

Source
pub struct AppWithState<S> { /* private fields */ }
Expand description

An Application that combines user-defined state-aware routes with the built-in App controller chain as a fallback.

Routes are matched in registration order. The first match wins; unmatched requests are forwarded to App (static files, health probes, etc.).

Implementations§

Source§

impl<S: Send + Sync + 'static> AppWithState<S>

Source

pub fn new(state: S) -> Self

Create a new AppWithState wrapping state.

state is stored behind an Arc so it can be shared across threads without cloning. Register routes with the builder methods.

Source

pub fn with_config(self, config: ServerConfig) -> Self

Pin the fallback App (used for any request this app’s own routes don’t match) to an explicit ServerConfig, instead of reading RWS_CONFIG_* environment variables per request.

Mirrors App::with_config — same rationale: safe for parallel tests (no test_env::lock() needed) and lets multiple differently-configured instances coexist in one process.

Source

pub fn state(&self) -> &S

Return a reference to the shared state.

Source

pub fn get<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,

Register a GET handler for pattern.

Source

pub fn post<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,

Register a POST handler for pattern.

Source

pub fn put<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,

Register a PUT handler for pattern.

Source

pub fn patch<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,

Register a PATCH handler for pattern.

Source

pub fn delete<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,

Register a DELETE handler for pattern.

Source

pub fn websocket<F>(self, pattern: &str, handler: F) -> Self
where F: Fn(WebSocketConnection, PathParams, Arc<S>) + Send + Sync + 'static,

Available on non-WebAssembly only.

Serve WebSocket connections at pattern: an upgrade request’s connection goes to handler with the route’s parameters and the state, anything else gets 426 Upgrade Required. The handler runs on a thread of its own for the connection’s life.

use std::sync::Mutex;
use rust_web_server::app::App;
use rust_web_server::websocket::Message;

let app = App::with_state(Mutex::new(0u64)).websocket("/count", |mut ws, _params, state| {
    while let Ok(Some(Message::Text(_))) = ws.recv() {
        let n = { let mut n = state.lock().unwrap(); *n += 1; *n };
        let _ = ws.send_text(&n.to_string());
    }
});
Source

pub fn stream_body(self, method: &str, pattern: &str) -> Self

Return a snapshot of all registered routes as (method, pattern) pairs. Hand requests to method pattern (same pattern syntax as the route) their body as a stream instead of reading it into memory first (#31): the handler reads request.body with std::io::Read while the upload is still arriving.

use std::io::Read;
use rust_web_server::app::App;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};

let app = App::with_state(())
    .put("/upload/:name", |request, params, _conn, _state| {
        let mut file = std::fs::File::create(format!("/tmp/{}", params.get("name").unwrap())).unwrap();
        let copied = std::io::copy(&mut &request.body, &mut file);
        Response::text(STATUS_CODE_REASON_PHRASE.n200_ok, &format!("{copied:?}"))
    })
    .stream_body("PUT", "/upload/:name");
Source

pub fn route_entries(&self) -> Vec<RouteInfo>

The registered routes as (method, pattern), in registration order.

Source

pub fn route<H: Handler<S>>(self, handler: H) -> Self

Register a typed handler (18.28.0): a function with extractor arguments annotated #[get("/path")] & co. The method and pattern come from the attribute, which checked the pattern’s parameters against the function’s Path argument at compile time.

ⓘ
use rust_web_server::handler::{Json, Path, State};

#[get("/posts/{id}")]
fn show_post(Path(id): Path<u64>, State(db): State<Db>) -> Result<Json<Post>, AppError> {
    db.find(id).map(Json).ok_or_else(|| AppError::NotFound(format!("post {id}")))
}

let app = App::with_state(db).route(show_post);
Source

pub fn route_conflicts(&self) -> Vec<RouteConflict>

Routes that never run because an earlier one with the same method matches every path they do (18.28.0). See Router::route_conflicts; each is also printed once to stderr when it’s registered.

Source

pub fn mcp( self, name: impl Into<String>, version: impl Into<String>, ) -> McpServer

Attach an MCP server to this application. Requests that do not match the MCP endpoint (POST /mcp) are forwarded to self, so all previously registered routes remain active.

use rust_web_server::app::App;
use rust_web_server::mcp::{McpContent, extract_arg};
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::core::New;

struct Db { url: String }

let app = App::with_state(Db { url: "postgres://localhost/mydb".to_string() })
    .get("/api/users", |_req, _params, _conn, _db| {
        let mut r = Response::new();
        r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
        r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
        r
    })
    .mcp("my-server", "1.0")
    .tool("list_users", "List all users", "{}", |_| {
        Ok(McpContent::json(r#"[{"id":1,"name":"Alice"}]"#))
    });
Source

pub fn wrap<M: Middleware + 'static>( self, layer: M, ) -> WithMiddleware<AppWithState<S>>

Wrap this application in a middleware layer.

Enables fluent composition:

use rust_web_server::app::App;
use rust_web_server::core::New;
use rust_web_server::middleware::RateLimitLayer;
use rust_web_server::response::Response;

let app = App::with_state(())
    .get("/ping", |_, _, _, _| Response::new())
    .wrap(RateLimitLayer);
Source

pub fn openapi_spec(&self, config: &OpenApiConfig) -> String

Available on crate feature openapi only.

Add GET /openapi.json (a generated OpenAPI 3.1 document covering every route registered so far) and GET /docs (Swagger UI, loaded from a CDN, pointed at /openapi.json).

Call this after registering your routes — routes added afterward still work but won’t appear in the generated spec, since it’s built once at this call rather than read dynamically per request.

Requires the openapi feature.

use rust_web_server::app::App;
use rust_web_server::openapi::OpenApiConfig;
use rust_web_server::response::Response;
use rust_web_server::core::New;

let app = App::with_state(())
    .get("/users", |_req, _params, _conn, _state| Response::new())
    .get("/users/:id", |_req, _params, _conn, _state| Response::new())
    .openapi(OpenApiConfig::new("My API", "1.0.0"));

This app’s OpenAPI document as JSON (what openapi serves), e.g. for McpServer::openapi_tools.

Source

pub fn openapi(self, config: OpenApiConfig) -> Self

Available on crate feature openapi only.

Trait Implementations§

Source§

impl<S: Send + Sync + 'static> Application for AppWithState<S>

Source§

fn runs_inline(&self, request: &Request, connection: &ConnectionInfo) -> bool

Route handlers are user code and may block: only requests no route matches, falling through to the built-in App, run inline.

Source§

fn execute( &self, request: &Request, connection: &ConnectionInfo, ) -> Result<Response, String>

Receives a parsed request and returns a fully-built response. Walk your controller list with is_matching / process and return the first match.
Source§

fn streams_body(&self, request: &Request, connection: &ConnectionInfo) -> bool

true to receive this request’s body as a stream (Body::Stream) instead of read into memory first: execute then runs while the body is still arriving, on a blocking-pool thread, and reads it with std::io::Read — a multi-gigabyte upload in a few chunks of memory. Asked after the head is read, so request.body is empty here; decide from the method, path and headers. Read more
Source§

fn registered_routes(&self) -> Vec<RouteInfo>

The routes this application registers, as (method, pattern) — what AppWithState::route_entries returns; wrappers (WithMiddleware, McpServer) report the routes of the application inside. For tools that describe an app (the dev MCP server’s routes tool), not for dispatch. Default: none. Since 18.28.0.
Source§

fn execute_async<'a>( &'a self, _request: &'a Request, _connection: &'a ConnectionInfo, ) -> Option<AsyncResponse<'a>>

Available on crate feature http2 only.
An asynchronous version of execute for this request, when the application has one: the async servers await it on the connection’s task instead of tying up a blocking-pool thread while it waits (the config-driven proxy uses this to wait for a backend without holding a thread). None (the default) means “use execute”. Wrappers return Some only when they add no synchronous work of their own.
Source§

fn middleware_names(&self) -> Vec<&'static str>

The names of the middleware layers around this application’s routes, in the order a request passes them (Middleware::layer_name). Default: none; WithMiddleware lists its layers, then the inner application’s. Since 18.28.0.
Source§

impl<S: Clone> Clone for AppWithState<S>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

§

impl<S> !RefUnwindSafe for AppWithState<S>

§

impl<S> !UnwindSafe for AppWithState<S>

§

impl<S> Freeze for AppWithState<S>
where Arc<S>: Freeze,

§

impl<S> Send for AppWithState<S>
where Arc<S>: Send,

§

impl<S> Sync for AppWithState<S>
where Arc<S>: Sync,

§

impl<S> Unpin for AppWithState<S>
where Arc<S>: Unpin,

§

impl<S> UnsafeUnpin for AppWithState<S>
where Arc<S>: 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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
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.
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