pub struct FluentRouter<State = ()> { /* private fields */ }Expand description
Fluent builder for axum::Router with configuration-based middleware setup.
This wrapper around axum::Router provides a fluent API for configuring middleware
and routes based on the application configuration. Create instances using
FluentRouter::without_state or FluentRouter::with_state.
The router forwards layering and nesting calls to the underlying axum::Router,
allowing middleware to be set up at any stage through dedicated setup_* methods.
If the configuration has a static files directory configured as a fallback,
it will be automatically set up. For all other directories, call
FluentRouter::setup_directories to install the necessary middleware.
§Graceful Shutdown
FluentRouter provides built-in support for graceful shutdown notifications.
Components can subscribe to shutdown events or use a cancellation token:
use axum_conf::{Config, FluentRouter, ShutdownPhase};
let router = FluentRouter::without_state(Config::default())?;
// Option 1: Simple cancellation token for background tasks
let token = router.cancellation_token();
tokio::spawn(async move {
loop {
tokio::select! {
_ = token.cancelled() => break,
_ = do_work() => {}
}
}
});
// Option 2: Subscribe to shutdown phases for complex cleanup
let mut rx = router.shutdown_notifier().subscribe();
tokio::spawn(async move {
while let Ok(phase) = rx.recv().await {
match phase {
ShutdownPhase::Initiated => println!("Shutting down..."),
_ => {}
}
}
});Implementations§
Source§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub fn setup_user_span(self) -> Self
pub fn setup_user_span(self) -> Self
Records the authenticated username to the logging span.
This middleware runs after authentication and records the username
to the user field of the current tracing span. This ensures all
subsequent logs within the request include the authenticated user.
Works with both Basic Auth (AuthenticatedIdentity) and OIDC
(KeycloakToken) authentication methods.
For unauthenticated requests (e.g., health endpoints), the user
field remains empty and won’t appear in log output.
Source§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub async fn setup_middleware(self) -> Result<Self>
pub async fn setup_middleware(self) -> Result<Self>
Sets up all standard middleware layers in the correct order.
This is the recommended way to configure middleware. It handles the complex ordering requirements automatically, ensuring all layers work correctly together.
§When to Use This Method
Use setup_middleware() for most applications. It provides production-ready
defaults and handles middleware dependencies automatically.
Use individual setup_* methods only when you need:
- Custom middleware ordering
- Middleware between specific layers
- Partial middleware stack (though
excludeconfig is preferred)
§What It Configures
- Liveness/readiness probes
- OIDC authentication (if
keycloakfeature enabled) - Request deduplication
- Concurrency limits
- Payload size limits
- Compression/decompression
- Path normalization
- Sensitive header protection
- Request ID generation
- API versioning
- CORS headers
- Security headers (Helmet)
- Logging and tracing
- Metrics collection (Prometheus)
- Request timeouts
- Rate limiting
- Panic recovery
§Middleware Order
CRITICAL: Middleware is processed outside-in for requests and inside-out for responses. The last layer added is the outermost layer and executes first on incoming requests.
The current order (from innermost to outermost):
- OIDC Authentication - Check auth after infrastructure layers
- Deduplication - Check for duplicate requests
- Concurrency limit - Control concurrent processing
- Max payload size - Limit body size
- Compression/Decompression - Handle encoding
- Path normalization - Normalize before routing
- Sensitive headers - Filter before logging
- API versioning - Extract version from path/headers/query
- CORS - Handle preflight & add headers
- Security headers (Helmet) - Apply to all responses
- Logging - Log all requests
- Metrics - Measure all requests
- Readiness - Database health check (benefits from timeout/rate limiting)
- Timeout - Set timeout boundary for everything (optional)
- Rate limiting - Reject excessive requests early
- Request ID - Generate/extract ID for tracing (early for observability)
- Liveness - Simple health check (always accessible, very early)
- Panic catching - Catch ALL panics from inner layers (outermost)
§Manual Setup (Advanced)
If you need custom ordering, call individual setup_* methods. Important rules:
- Call order matters: Methods must be called in reverse execution order (first method called = innermost layer = executes last on request)
- Dependencies: Some middleware depends on others:
setup_request_id()must be called aftersetup_deduplication()so the request ID is available when deduplication checks for duplicatessetup_oidc()requiressetup_session_handling()(when using sessions)
- Don’t call twice: Each
setup_*method should only be called once - Configuration controls: Use
[http.middleware] exclude/includeinstead of skipping methods, as this ensures proper dependency handling
// Manual setup example (not recommended unless you need custom ordering)
let router = FluentRouter::without_state(Config::default())?
// Innermost layers first (execute last on request)
.setup_deduplication()
.setup_logging()
.setup_readiness() // /ready - after timeout/rate limiting (benefits from protection)
.setup_timeout()
.setup_rate_limiting()
.setup_request_id() // Outer to deduplication, generates ID early
.setup_liveness() // /live - always accessible, very early
.setup_catch_panic(); // Outermost (executes first on request)§Returns
A Result containing the configured router or an error if setup fails.
§Errors
Returns an error if:
- OIDC configuration is invalid (when
keycloakfeature enabled) - Configuration validation fails
§Note
Disable Prometheus in tests to avoid global registry conflicts:
let mut config = Config::default();
config.http.with_metrics = false;Sourcepub fn build(self) -> Self
👎Deprecated since 0.2.2: Use setup_middleware() instead, which now includes all layers
pub fn build(self) -> Self
Adds the remaining standard middleware layers in the correct order. These layers should be added last as they handle security, errors and panics. Since they are added last, they are the outermost layers and thus executed first.
§Deprecated
This method is deprecated. Use setup_middleware() instead, which now includes
all middleware layers in the optimal order. This method is kept for backward
compatibility but does nothing.
Sourcepub async fn start(self) -> Result<()>
pub async fn start(self) -> Result<()>
Starts the HTTP server based on the current configuration.
The server supports both HTTP/1.1 and HTTP/2 protocols automatically. HTTP/2 will be used when clients request it via ALPN negotiation.
§Graceful Shutdown
When a shutdown signal is received (SIGTERM or SIGINT), the server:
- Emits
ShutdownPhase::Initiatedto all subscribers - Triggers the cancellation token (stopping background tasks)
- Stops accepting new connections
- Emits
ShutdownPhase::GracePeriodStartedwith the configured timeout - Waits for in-flight requests to complete (up to
shutdown_timeout) - Emits
ShutdownPhase::GracePeriodEndedif timeout expires - Exits
If all connections drain before the timeout, shutdown completes early without waiting for the full timeout duration.
Components can subscribe to these phases before calling start():
use axum_conf::{Config, FluentRouter, ShutdownPhase};
let router = FluentRouter::without_state(Config::default())?;
// Set up shutdown handlers BEFORE starting
let mut shutdown_rx = router.subscribe_to_shutdown();
tokio::spawn(async move {
while let Ok(phase) = shutdown_rx.recv().await {
tracing::info!("Shutdown phase: {:?}", phase);
}
});
// Now start the server
router.setup_middleware().await?.start().awaitSourcepub fn layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
pub fn layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
Adds a custom Tower middleware layer to the router.
This is a low-level method that forwards to axum::Router::layer(),
allowing you to add custom middleware that isn’t provided by the library.
§Type Parameters
L- A Tower Layer that produces services compatible with Axum
§Examples
use tower::limit::ConcurrencyLimitLayer;
let router = FluentRouter::without_state(Config::default())?
.layer(ConcurrencyLimitLayer::new(100));Sourcepub fn route(self, path: &str, route: MethodRouter<State>) -> Self
pub fn route(self, path: &str, route: MethodRouter<State>) -> Self
Adds a new route to the router at the specified path.
Routes define how HTTP requests to specific paths are handled.
Use the routing helpers from axum::routing to create method routers:
get()- Handle GET requestspost()- Handle POST requestsput()- Handle PUT requestsdelete()- Handle DELETE requests- And more…
§Arguments
path- The URL path pattern for this route (e.g., “/users/:id”)route- AMethodRoutercreated withaxum::routinghelpers
§Examples
use axum_conf::{Config, FluentRouter};
use axum::routing::get;
async fn handler() -> &'static str {
"Hello, World!"
}
let config = Config::default();
let router = FluentRouter::without_state(config)
.unwrap()
.route("/hello", get(handler))
.into_inner();Sourcepub fn route_layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
pub fn route_layer<L>(self, layer: L) -> Selfwhere
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<Infallible> + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
Adds a middleware layer that only applies to routes, not services.
This is a low-level method that forwards to axum::Router::route_layer().
Unlike layer(), this only affects route handlers and doesn’t wrap
nested services.
§Type Parameters
L- A Tower Layer that produces services compatible with Axum
§Use Cases
Use this when you want middleware to only affect your route handlers
but not services like ServeDir or nested routers.
Sourcepub fn nest(self, path: &str, router: Router<State>) -> Self
pub fn nest(self, path: &str, router: Router<State>) -> Self
Nests another router at a specific path prefix.
All routes in the nested router will be prefixed with the given path. Middleware added to the nested router only affects its own routes.
§Arguments
path- The path prefix (must start with/)router- The router to nest
§Examples
use axum::{Router, routing::get};
let api_v1 = Router::new()
.route("/users", get(|| async { "users" }));
let app = FluentRouter::without_state(Config::default())?
.nest("/api/v1", api_v1); // Routes at /api/v1/usersSourcepub fn nest_service<T>(self, path: &str, service: T) -> Self
pub fn nest_service<T>(self, path: &str, service: T) -> Self
Nests a Tower service at a specific path prefix.
Similar to nest() but for raw Tower services instead of Axum routers.
Commonly used for serving static files with ServeDir.
§Arguments
path- The path prefix (must start with/)service- The Tower service to nest
§Examples
use axum::{Router, routing::get};
let service = Router::new().route("/health", get(|| async { "OK" }));
let app = FluentRouter::without_state(Config::default())?
.nest_service("/api", service);Sourcepub fn merge(self, other: Router<State>) -> Self
pub fn merge(self, other: Router<State>) -> Self
Merges another router into this one.
Routes and services from the other router are added to this router.
Unlike nest(), routes are not prefixed - they’re added at the same level.
§Arguments
other- The router to merge
§Examples
use axum::{Router, routing::get};
let user_routes = Router::new()
.route("/users", get(|| async { "users" }));
let app = FluentRouter::without_state(Config::default())?
.merge(user_routes); // Routes directly at /users§Common Pattern
Use merge() to combine route modules:
FluentRouter::without_state(Config::default())?
.merge(api_routes())
.merge(admin_routes());Sourcepub fn route_service<T>(self, path: &str, service: T) -> Self
pub fn route_service<T>(self, path: &str, service: T) -> Self
Adds a Tower service at a specific route.
Unlike nest_service(), this adds the service at an exact path rather
than a path prefix.
§Arguments
path- The exact route pathservice- The Tower service to add
§Examples
use tower::service_fn;
use http::Response;
let service = service_fn(|_req| async {
Ok::<_, std::convert::Infallible>(Response::new("Hello".into()))
});
let app = FluentRouter::without_state(Config::default())?
.route_service("/custom", service);Sourcepub fn into_inner(self) -> Router<State>
pub fn into_inner(self) -> Router<State>
Consumes the FluentRouter and returns the underlying axum::Router.
Use this when you need direct access to the Axum router, typically for
testing or when you want to add additional middleware that requires
the concrete Router type.
§Examples
let fluent = FluentRouter::without_state(Config::default())?;
let axum_router: axum::Router = fluent.into_inner();Source§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub fn setup_rate_limiting(self) -> Self
pub fn setup_rate_limiting(self) -> Self
No-op when rate-limiting feature is disabled.
Sourcepub fn setup_catch_panic(self) -> Self
pub fn setup_catch_panic(self) -> Self
Sets up panic catching middleware.
Catches panics in request handlers and returns a 500 Internal Server Error
response instead of crashing the server. Optionally sends panic details to
a notification channel if configured with with_panic_notification_channel().
§Panic Handling
When a handler panics:
- Panic is caught before it crashes the server
- Client receives 500 response
- Panic message is sent to notification channel (if configured)
- Server continues running
§Examples
let (tx, rx) = tokio::sync::mpsc::channel(100);
FluentRouter::without_state(Config::default())?
.with_panic_notification_channel(tx)
.setup_catch_panic();§Production Use
Essential for production to prevent panics from taking down the server. That’s why this middleware cannot be disabled.
This middleware is automatically included in setup_middleware() as the
outermost layer to ensure ALL panics are caught.
Source§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub fn setup_path_normalization(self) -> Self
pub fn setup_path_normalization(self) -> Self
No-op when path-normalization feature is disabled.
Sourcepub fn setup_timeout(self) -> Self
pub fn setup_timeout(self) -> Self
Sets up request timeout middleware.
Aborts requests that take longer than the configured duration with a
408 Request Timeout response.
§Configuration
[http]
request_timeout = "30s" # Optional, uses humantime format§Use Cases
- Prevent slow requests from tying up resources
- Ensure predictable response times
- Protect against slowloris attacks
Sourcepub fn setup_api_versioning(self, _default_version: u32) -> Self
pub fn setup_api_versioning(self, _default_version: u32) -> Self
No-op when api-versioning feature is disabled.
Sourcepub fn setup_helmet(self) -> Self
pub fn setup_helmet(self) -> Self
No-op when security-headers feature is disabled.
Sourcepub fn setup_compression(self) -> Self
pub fn setup_compression(self) -> Self
No-op when compression feature is disabled.
Sourcepub fn setup_cors(self) -> Self
pub fn setup_cors(self) -> Self
No-op when cors feature is disabled.
Sourcepub fn setup_liveness(self) -> Self
pub fn setup_liveness(self) -> Self
Sets up the Kubernetes liveness probe endpoint.
Adds a simple endpoint that always returns 200 OK to indicate the process is running. This endpoint is placed very early in the middleware stack (after panic catching) so it remains accessible even when other middleware fails.
§Configuration
[http]
liveness_route = "/live" # Default§Kubernetes Integration
livenessProbe:
httpGet:
path: /live
port: 3000Sourcepub fn setup_readiness(self) -> Self
pub fn setup_readiness(self) -> Self
Sets up the Kubernetes readiness probe endpoint.
Adds an endpoint that returns 200 OK if the service can handle traffic.
When the postgres feature is enabled and a database is configured,
this endpoint verifies database connectivity by executing a simple query.
If the database is unreachable, returns 503 Service Unavailable.
When the circuit-breaker feature is also enabled, the endpoint first checks
if the database circuit breaker is open. If the circuit is open, it returns
503 immediately without attempting a database query, preventing additional
load on a failing database.
This endpoint is placed after rate limiting and timeout middleware so that:
- Excessive health check requests don’t overwhelm the service
- Database queries have a timeout to prevent hanging probes
§Configuration
[http]
readiness_route = "/ready" # Default§Kubernetes Integration
readinessProbe:
httpGet:
path: /ready
port: 3000Sourcepub fn setup_liveness_readiness(self) -> Self
👎Deprecated since 0.4.0: Use setup_middleware() or call setup_liveness() and setup_readiness() separately for optimal middleware ordering
pub fn setup_liveness_readiness(self) -> Self
Sets up both Kubernetes health check endpoints.
This is a convenience method that calls both setup_liveness
and setup_readiness. However, when using setup_middleware(),
these endpoints are placed at different positions in the middleware stack for optimal
behavior.
§Deprecated
Prefer using setup_middleware() which places liveness and readiness endpoints at
their optimal positions in the middleware stack. If you need manual control, use
setup_liveness() and setup_readiness() separately.
§Configuration
[http]
liveness_route = "/live" # Default
readiness_route = "/ready" # DefaultSource§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub fn setup_metrics(self) -> Self
pub fn setup_metrics(self) -> Self
No-op when metrics feature is disabled.
Sourcepub fn setup_logging(self) -> Self
pub fn setup_logging(self) -> Self
Sets up HTTP request/response logging middleware.
Adds structured tracing for all HTTP requests, logging:
- Request method and path
- Response status code
- Request duration
- Client IP address
- Request ID (when available)
Log output format is controlled by the logging.format configuration.
Request IDs are automatically included in the trace span context.
When OpenTelemetry is enabled, extracts trace context from incoming W3C traceparent headers and propagates it through the application for distributed tracing.
Source§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub fn setup_max_payload_size(self) -> Self
pub fn setup_max_payload_size(self) -> Self
No-op when payload-limit feature is disabled.
Sourcepub fn setup_concurrency_limit(self) -> Self
pub fn setup_concurrency_limit(self) -> Self
No-op when concurrency-limit feature is disabled.
Sourcepub fn setup_request_id(self) -> Self
pub fn setup_request_id(self) -> Self
Sets up request ID generation and propagation.
Adds two middleware layers:
- Generates or preserves
x-request-idheaders - Propagates the request ID to response headers
Request IDs are UUIDv7 values that enable:
- Distributed tracing across services
- Log correlation
- Request debugging
If a request already has an x-request-id header, it is preserved.
Sourcepub fn setup_deduplication(self) -> Self
pub fn setup_deduplication(self) -> Self
No-op when deduplication feature is disabled.
Sourcepub fn setup_sensitive_headers(self) -> Self
pub fn setup_sensitive_headers(self) -> Self
No-op when sensitive-headers feature is disabled.
Source§impl FluentRouter
impl FluentRouter
Sourcepub fn without_state<T>(config: Config<T>) -> Result<FluentRouter<()>>
pub fn without_state<T>(config: Config<T>) -> Result<FluentRouter<()>>
Creates a new FluentRouter without application state.
Accepts any Config<T> and extracts the base configuration fields,
discarding the application-specific config. To preserve your app config,
clone config.app before calling this method.
§Example
use axum_conf::{Config, FluentRouter};
use serde::Deserialize;
#[derive(Debug, Clone, Default, Deserialize)]
struct MyAppConfig {
#[serde(default)]
api_key: String,
}
let config: Config<MyAppConfig> = Config::default();
let my_settings = config.app.clone(); // Preserve app config
let router = FluentRouter::without_state(config)?;
// Use my_settings in your handlers via stateSource§impl<State> FluentRouter<State>
impl<State> FluentRouter<State>
Sourcepub fn with_state<S, T>(config: Config<T>, state: S) -> Result<FluentRouter<S>>
pub fn with_state<S, T>(config: Config<T>, state: S) -> Result<FluentRouter<S>>
Creates a new FluentRouter with the provided configuration.
Validates the configuration and sets up any fallback static file directories.
Other static directories must be set up explicitly using setup_directories().
If a configuration for a database pool is provided, the pool will be created
and made available for health checks. It can also be accessed via a call to
db_pool().
Accepts any Config<T> and extracts the base configuration fields,
discarding the application-specific config. To preserve your app config,
clone config.app before calling this method.
§Arguments
config- The service configuration (with any app-specific type)state- The application state to be shared across handlers
§Returns
A Result containing the new FluentRouter or an error if configuration is invalid.
§Errors
Returns an error if:
- Configuration validation fails
- Fallback directories are marked as protected
- Required configuration values are missing
Sourcepub fn shutdown_notifier(&self) -> &ShutdownNotifier
pub fn shutdown_notifier(&self) -> &ShutdownNotifier
Returns a reference to the shutdown notifier.
Use this to subscribe to shutdown phase notifications for coordinated cleanup across multiple components.
§Example
use axum_conf::{Config, FluentRouter, ShutdownPhase};
let router = FluentRouter::without_state(Config::default())?;
let notifier = router.shutdown_notifier();
// Subscribe from multiple places
let mut rx1 = notifier.subscribe();
let mut rx2 = notifier.subscribe();
// Each subscriber receives all phases
tokio::spawn(async move {
while let Ok(phase) = rx1.recv().await {
match phase {
ShutdownPhase::Initiated => {
// Close external connections
}
ShutdownPhase::GracePeriodStarted { timeout } => {
// Log remaining time
}
ShutdownPhase::GracePeriodEnded => {
// Flush buffers
}
}
}
});Sourcepub fn cancellation_token(&self) -> CancellationToken
pub fn cancellation_token(&self) -> CancellationToken
Returns a cancellation token that is triggered when shutdown begins.
This is a convenience method equivalent to calling
router.shutdown_notifier().cancellation_token().
The token is triggered when the server receives a shutdown signal (SIGTERM/SIGINT). Use it in background tasks to gracefully stop work.
§Example
use axum_conf::{Config, FluentRouter};
use std::time::Duration;
let router = FluentRouter::without_state(Config::default())?;
let token = router.cancellation_token();
// Background task that respects shutdown
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(60));
loop {
tokio::select! {
_ = token.cancelled() => {
tracing::info!("Periodic task stopping");
break;
}
_ = interval.tick() => {
// Do periodic work
tracing::debug!("Running periodic task");
}
}
}
});§Multiple Tokens
Each call returns a new clone of the token. All tokens share the same cancellation state - when one is cancelled, all are cancelled:
let router = FluentRouter::without_state(Config::default())?;
let token1 = router.cancellation_token();
let token2 = router.cancellation_token();
// Both tokens will be cancelled simultaneously on shutdownSourcepub fn subscribe_to_shutdown(&self) -> Receiver<ShutdownPhase>
pub fn subscribe_to_shutdown(&self) -> Receiver<ShutdownPhase>
Returns a receiver for shutdown phase notifications.
This is a convenience method equivalent to calling
router.shutdown_notifier().subscribe().
Each call creates a new independent subscriber. Subscribers created after a phase is emitted will not receive that phase.
§Example
use axum_conf::{Config, FluentRouter, ShutdownPhase};
let router = FluentRouter::without_state(Config::default())?;
let mut rx = router.subscribe_to_shutdown();
tokio::spawn(async move {
while let Ok(phase) = rx.recv().await {
tracing::info!("Shutdown phase: {:?}", phase);
}
});Sourcepub fn with_panic_notification_channel(self, ch: Sender<String>) -> Self
pub fn with_panic_notification_channel(self, ch: Sender<String>) -> Self
Sets a notification channel for panic messages.
When configured, any panics caught by the panic handler middleware will send a message to this channel. Useful for monitoring, alerting, or logging panic events in production.
§Arguments
ch- A tokio mpsc sender for panic notification messages
§Examples
let (tx, mut rx) = tokio::sync::mpsc::channel(100);
let config = Config::default();
let router = FluentRouter::without_state(config)?
.with_panic_notification_channel(tx);
// In another task, receive panic notifications
tokio::spawn(async move {
while let Some(panic_msg) = rx.recv().await {
eprintln!("Panic caught: {}", panic_msg);
}
});Sourcepub fn setup_directories(self, protected: bool) -> Result<Self>
pub fn setup_directories(self, protected: bool) -> Result<Self>
Sets up all static directories configured in the HTTP section except the fallback one. If protected is true, only protected directories will be added. Otherwise only public directories are added.
Sourcepub fn setup_fallback_files(self) -> Result<Self>
pub fn setup_fallback_files(self) -> Result<Self>
Sets up a fallback static file directory if configured.
Sourcepub fn setup_public_files(self) -> Result<Self>
pub fn setup_public_files(self) -> Result<Self>
Sets up public (unprotected) static file directories.
Convenience method that calls setup_directories(false) to serve
static files that don’t require authentication.
§Examples
[[http.directories]]
directory = "./public"
route = "/static"
protected = falseSourcepub fn setup_protected_files(self) -> Result<Self>
pub fn setup_protected_files(self) -> Result<Self>
Sets up protected static file directories that require authentication.
Convenience method that calls setup_directories(true) to serve
static files only to authenticated users. Must be called after
authentication middleware is set up.
§Examples
[[http.directories]]
directory = "./private"
route = "/downloads"
protected = true