pub struct App { /* private fields */ }Expand description
The built-in HTTP application. Serves static files, favicons, forms, file uploads, health probes, metrics, and a 404 fallback.
Use as-is or compose with the framework’s building blocks:
use rust_web_server::app::App;
use rust_web_server::middleware::{WithMiddleware, RateLimitLayer};
use rust_web_server::core::New;
// Middleware stack around the built-in app
let app = App::new().wrap(RateLimitLayer);For user-defined routes with shared state, call App::with_state:
use rust_web_server::app::App;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::core::New;
struct State { version: &'static str }
let app = App::with_state(State { version: "1.0" })
.get("/version", |_req, _params, _conn, state| {
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
});§Per-instance configuration
By default App::new() reads CORS, CSP, and other settings from
environment variables on each request (matching the current process state,
including hot-reloaded values). Call App::with_config to pin the
configuration to a specific ServerConfig — this is the recommended
pattern for parallel integration tests, which should not touch environment
variables at all.
use rust_web_server::app::App;
use rust_web_server::server_config::ServerConfig;
use rust_web_server::test_client::TestClient;
// No env writes, no lock needed — safe to run in parallel.
let app = App::with_config(ServerConfig {
cors_allow_all: false,
cors_allow_origins: "https://trusted.example.com".to_string(),
..ServerConfig::default()
});
let client = TestClient::new(app);Implementations§
Source§impl App
impl App
Sourcepub fn with_config(config: ServerConfig) -> Self
pub fn with_config(config: ServerConfig) -> Self
Create an App pinned to an explicit ServerConfig.
All CORS, CSP, and other header settings are taken from config rather
than from environment variables. The configuration is fixed for the
lifetime of the App instance — SIGHUP / hot-reload do not affect it.
This is the preferred constructor for integration tests: build a
ServerConfig with the exact settings under test, pass it here, and
use TestClient to drive requests. No environment writes and no
test_env::lock() are needed.
Source§impl App
impl App
Sourcepub fn handle_request(request: Request) -> (Response, Request)
pub fn handle_request(request: Request) -> (Response, Request)
Dispatch request through the controller chain and return the response.
This is a convenience wrapper over Application::execute that uses a
synthetic loopback ConnectionInfo. Use it in tests or when no real
connection context is available. Prefer TestClient for structured
test code.
Sourcepub fn with_state<S: Send + Sync + 'static>(state: S) -> AppWithState<S>
pub fn with_state<S: Send + Sync + 'static>(state: S) -> AppWithState<S>
Create a state-aware application. Routes registered on the returned
AppWithState<S> are tried first; unmatched requests fall through to
the built-in controller chain (static files, health probes, etc.).
The state is stored as Arc<S> and shared across all handlers.
§Example
use rust_web_server::app::App;
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://...".to_string() })
.get("/ping", |_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
});Sourcepub fn wrap<M: Middleware + 'static>(self, layer: M) -> WithMiddleware<App>
pub fn wrap<M: Middleware + 'static>(self, layer: M) -> WithMiddleware<App>
Wrap this application in a middleware layer.
Returns a WithMiddleware<App> that runs layer before every
request. Chain .wrap() calls to stack multiple layers:
use rust_web_server::app::App;
use rust_web_server::middleware::RateLimitLayer;
use rust_web_server::core::New;
let app = App::new().wrap(RateLimitLayer);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. Tools, resources, and
prompts are registered on the returned McpServer; requests that
do not match the MCP endpoint are forwarded to self (static files,
health probes, any custom routes registered before this call).
use rust_web_server::app::App;
use rust_web_server::mcp::{McpContent, extract_arg};
use rust_web_server::core::New;
// Pure MCP — unmatched paths handled by built-in App
let app = App::new()
.mcp("my-server", "1.0")
.tool(
"echo",
"Echo text back",
r#"{"type":"object","properties":{"text":{"type":"string"}}}"#,
|args| Ok(McpContent::text(extract_arg(args, "text").unwrap_or_default())),
);To combine with custom HTTP routes, start from App::with_state:
use rust_web_server::app::App;
use rust_web_server::mcp::McpContent;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::core::New;
let app = App::with_state(())
.get("/api/ping", |_, _, _, _| {
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("ping", "Ping the server", "{}", |_| Ok(McpContent::text("pong")));Sourcepub fn with_async_state<S: Send + Sync + 'static>(
state: S,
) -> AsyncAppWithState<S>
pub fn with_async_state<S: Send + Sync + 'static>( state: S, ) -> AsyncAppWithState<S>
Create an async state-aware application (requires the http2 feature).
Handlers are async fn closures that can await database queries,
HTTP clients, or any other async I/O. Unmatched routes fall through to
the built-in controller chain.
§Example
use std::sync::Arc;
use rust_web_server::app::App;
use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
use rust_web_server::core::New;
struct Db { url: String }
let app = App::with_async_state(Db { url: "postgres://...".to_string() })
.get("/ping", |_req, _params, _conn, state| async move {
// state: Arc<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
});