{% extends "docs_base.html" %}
{% block content %}
<div class="prose prose-slate max-w-none">
<h1>Middleware in Ruskit</h1>
<p>Middleware provides a convenient mechanism for filtering HTTP requests entering your application. For example, Ruskit includes middleware for handling CORS and trimming strings. You may add your own middleware to customize it further.</p>
<h2>Introduction</h2>
<p>Middleware acts as a bridge between a request and a response, allowing you to:</p>
<ul>
<li>Modify requests before they reach your route handlers</li>
<li>Modify responses before they are sent back to the client</li>
<li>Perform actions before or after request handling</li>
<li>Terminate requests early if certain conditions aren't met</li>
</ul>
<h2>Using Built-in Middleware</h2>
<h3>Available Middleware</h3>
<p>Ruskit comes with several built-in middleware components:</p>
<h4>CORS Middleware</h4>
<p>Handles Cross-Origin Resource Sharing headers:</p>
<pre><code class="language-rust">use ruskit::presets::Cors;
Router::new()
.route(
"/api",
get(handler).middleware(Cors::new("http://example.com"))
)</code></pre>
<p>Configure CORS with additional options:</p>
<pre><code class="language-rust">let cors = Cors::new("http://example.com")
.with_methods("GET, POST, PUT, DELETE")
.with_headers("Content-Type, Authorization");</code></pre>
<h4>TrimStrings Middleware</h4>
<p>Automatically trims string inputs in requests:</p>
<pre><code class="language-rust">use ruskit::presets::TrimStrings;
Router::new()
.route(
"/users",
post(create_user).middleware(TrimStrings::new())
)</code></pre>
<h3>Applying Middleware</h3>
<p>There are three ways to apply middleware in Ruskit:</p>
<h4>1. Route Middleware</h4>
<p>Apply middleware to specific routes:</p>
<pre><code class="language-rust">use ruskit::presets::{Cors, TrimStrings};
Router::new()
.route(
"/api/users",
get(users_index)
.middleware(TrimStrings::new())
.middleware(Cors::new("http://example.com"))
)</code></pre>
<h4>2. Global Middleware</h4>
<p>Apply middleware to every HTTP request:</p>
<pre><code class="language-rust">// In your bootstrap.rs
pub async fn bootstrap() {
let app = Application::instance().await;
let mut app = app.write().await;
// Configure global middleware
app.middleware(|stack| {
stack.add(Middleware::Cors(Cors::new("*")));
stack.add(Middleware::TrimStrings(TrimStrings::new()));
}).await;
}</code></pre>
<h4>3. Middleware Groups</h4>
<p>Group middleware for specific sets of routes:</p>
<pre><code class="language-rust">// Define middleware groups
app.middleware_groups(|groups| {
groups.push((
"api",
vec![
Middleware::Cors(Cors::new("http://api.example.com")),
Middleware::TrimStrings(TrimStrings::new())
]
));
}).await;
// Use middleware group
if let Some(middlewares) = middleware_group("api").await {
router = router.middlewares(middlewares);
}</code></pre>
<h2>Creating Custom Middleware</h2>
<h3>Basic Structure</h3>
<p>Create a new middleware by implementing a struct with a handle method:</p>
<pre><code class="language-rust">use axum::{
middleware::Next,
response::Response,
http::Request,
body::Body,
};
#[derive(Clone)]
pub struct LogRequest;
impl LogRequest {
pub fn new() -> Self {
Self
}
pub(crate) async fn handle(
&self,
request: Request<Body>,
next: Next,
) -> Result<Response, Response> {
println!("Incoming request to: {}", request.uri());
let response = next.run(request).await;
println!("Outgoing response");
Ok(response)
}
}</code></pre>
<h3>Registering Custom Middleware</h3>
<p>1. Add your middleware to the internal Middleware enum:</p>
<pre><code class="language-rust">// In framework/middleware/internal.rs
pub enum Middleware {
Cors(presets::Cors),
TrimStrings(presets::TrimStrings),
LogRequest(LogRequest), // Add your variant
}</code></pre>
<p>2. Implement the From trait:</p>
<pre><code class="language-rust">impl From<LogRequest> for Middleware {
fn from(middleware: LogRequest) -> Self {
Self::LogRequest(middleware)
}
}</code></pre>
<h3>Using Custom Middleware</h3>
<pre><code class="language-rust">use crate::middleware::LogRequest;
Router::new()
.route("/api", get(handler).middleware(LogRequest::new()))</code></pre>
<h2>Best Practices</h2>
<ol>
<li>
<strong>Order of Middleware</strong>
<ul>
<li>Place middleware that modifies the request before middleware that uses the request</li>
<li>Authentication/Authorization middleware should typically run early</li>
<li>Logging middleware often works best at the start or end of the chain</li>
</ul>
</li>
<li>
<strong>Performance Considerations</strong>
<ul>
<li>Keep middleware logic efficient</li>
<li>Only use middleware where needed</li>
<li>Consider the impact of middleware on response times</li>
</ul>
</li>
<li>
<strong>Error Handling</strong>
<ul>
<li>Use the Result type to properly handle errors</li>
<li>Return appropriate error responses</li>
<li>Consider logging middleware errors for debugging</li>
</ul>
</li>
<li>
<strong>State Management</strong>
<ul>
<li>Use Clone or Arc for sharing state between middleware instances</li>
<li>Keep middleware stateless when possible</li>
<li>Use configuration structs for middleware that needs configuration</li>
</ul>
</li>
</ol>
<h2>Middleware Execution Flow</h2>
<p>The middleware execution follows this pattern:</p>
<ol>
<li>Request enters the application</li>
<li>Global middleware executes in order of registration</li>
<li>Group middleware executes (if applicable)</li>
<li>Route-specific middleware executes</li>
<li>Route handler executes</li>
<li>Middleware executes in reverse order for the response</li>
<li>Response leaves the application</li>
</ol>
<h2>Debugging Middleware</h2>
<p>To debug middleware:</p>
<p>1. Use logging to track execution:</p>
<pre><code class="language-rust">println!("Middleware: Processing request to {}", request.uri());</code></pre>
<p>2. Check middleware order:</p>
<pre><code class="language-rust">// Explicit ordering
router
.middleware(first_middleware)
.middleware(second_middleware)
.middleware(third_middleware)</code></pre>
<p>3. Test middleware in isolation:</p>
<pre><code class="language-rust">#[cfg(test)]
mod tests {
#[tokio::test]
async fn test_middleware() {
let middleware = MyMiddleware::new();
let request = Request::builder()
.uri("/test")
.body(Body::empty())
.unwrap();
let response = middleware
.handle(request, next)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
}</code></pre>
</div>
{% endblock %}