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> AppWithState<S>
impl<S> AppWithState<S>
Sourcepub fn new(state: S) -> AppWithState<S>
pub fn new(state: S) -> AppWithState<S>
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.
Sourcepub fn with_config(self, config: ServerConfig) -> AppWithState<S>
pub fn with_config(self, config: ServerConfig) -> AppWithState<S>
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.
Sourcepub fn get<F>(self, pattern: &str, handler: F) -> AppWithState<S>
pub fn get<F>(self, pattern: &str, handler: F) -> AppWithState<S>
Register a GET handler for pattern.
Sourcepub fn post<F>(self, pattern: &str, handler: F) -> AppWithState<S>
pub fn post<F>(self, pattern: &str, handler: F) -> AppWithState<S>
Register a POST handler for pattern.
Sourcepub fn put<F>(self, pattern: &str, handler: F) -> AppWithState<S>
pub fn put<F>(self, pattern: &str, handler: F) -> AppWithState<S>
Register a PUT handler for pattern.
Sourcepub fn patch<F>(self, pattern: &str, handler: F) -> AppWithState<S>
pub fn patch<F>(self, pattern: &str, handler: F) -> AppWithState<S>
Register a PATCH handler for pattern.
Sourcepub fn delete<F>(self, pattern: &str, handler: F) -> AppWithState<S>
pub fn delete<F>(self, pattern: &str, handler: F) -> AppWithState<S>
Register a DELETE handler for pattern.
Sourcepub fn websocket<F>(self, pattern: &str, handler: F) -> AppWithState<S>
Available on non-WebAssembly only.
pub fn websocket<F>(self, pattern: &str, handler: F) -> AppWithState<S>
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());
}
});Sourcepub fn stream_body(self, method: &str, pattern: &str) -> AppWithState<S>
pub fn stream_body(self, method: &str, pattern: &str) -> AppWithState<S>
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");Sourcepub fn route_entries(&self) -> Vec<RouteInfo>
pub fn route_entries(&self) -> Vec<RouteInfo>
The registered routes as (method, pattern), in registration order.
Sourcepub fn route<H>(self, handler: H) -> AppWithState<S>where
H: Handler<S>,
pub fn route<H>(self, handler: H) -> AppWithState<S>where
H: Handler<S>,
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);Sourcepub fn route_conflicts(&self) -> Vec<RouteConflict>
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.
Sourcepub fn mcp(
self,
name: impl Into<String>,
version: impl Into<String>,
) -> McpServer
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"}]"#))
});Sourcepub fn wrap<M>(self, layer: M) -> WithMiddleware<AppWithState<S>>where
M: Middleware + 'static,
pub fn wrap<M>(self, layer: M) -> WithMiddleware<AppWithState<S>>where
M: Middleware + 'static,
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);Sourcepub fn openapi_spec(&self, config: &OpenApiConfig) -> String
Available on crate feature openapi only.
pub fn openapi_spec(&self, config: &OpenApiConfig) -> String
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.
pub fn openapi(self, config: OpenApiConfig) -> AppWithState<S>
openapi only.Trait Implementations§
Source§impl<S> Application for AppWithState<S>
impl<S> Application for AppWithState<S>
Source§fn runs_inline(&self, request: &Request, connection: &ConnectionInfo) -> bool
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>
fn execute( &self, request: &Request, connection: &ConnectionInfo, ) -> Result<Response, String>
is_matching / process and return the first match.Source§fn streams_body(&self, request: &Request, connection: &ConnectionInfo) -> bool
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 moreSource§fn registered_routes(&self) -> Vec<RouteInfo>
fn registered_routes(&self) -> Vec<RouteInfo>
(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<Pin<Box<dyn Future<Output = Result<Response, String>> + Send + 'a>>>
fn execute_async<'a>( &'a self, _request: &'a Request, _connection: &'a ConnectionInfo, ) -> Option<Pin<Box<dyn Future<Output = Result<Response, String>> + Send + 'a>>>
http2 only.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>
fn middleware_names(&self) -> Vec<&'static str>
Middleware::layer_name).
Default: none; WithMiddleware lists its layers, then the inner
application’s. Since 18.28.0.Source§impl<S> Clone for AppWithState<S>where
S: Clone,
impl<S> Clone for AppWithState<S>where
S: Clone,
Source§fn clone(&self) -> AppWithState<S>
fn clone(&self) -> AppWithState<S>
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl<S> !RefUnwindSafe for AppWithState<S>
impl<S> !UnwindSafe for AppWithState<S>
impl<S> Freeze for AppWithState<S>
impl<S> Send for AppWithState<S>
impl<S> Sync for AppWithState<S>
impl<S> Unpin for AppWithState<S>
impl<S> UnsafeUnpin for AppWithState<S>where
Arc<S>: UnsafeUnpin,
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
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
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<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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 moreSource§impl<T> Pointable for T
impl<T> Pointable for T
impl<T> Read<Exclusive, BecauseExclusive> for Twhere
T: ?Sized,
Source§impl<T> ServiceExt for T
impl<T> ServiceExt for T
Source§fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>where
Self: Sized,
fn map_response_body<F>(self, f: F) -> MapResponseBody<Self, F>where
Self: Sized,
map-response-body only.Source§fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
fn trace_for_http(self) -> Trace<Self, SharedClassifier<ServerErrorsAsFailures>>where
Self: Sized,
trace only.Source§fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
fn trace_for_grpc(self) -> Trace<Self, SharedClassifier<GrpcErrorsAsFailures>>where
Self: Sized,
trace only.