️ Middlewares
Middleware functions are used to intercept requests before they reach the main logic of the application. In Lila, core middlewares are defined in the core/middleware.py file. Middlewares can be used for tasks such as authentication, logging, security, and error handling.
Lila uses a unified Middleware
manager that provides both class-based HTTP middlewares and route decorators.
Default Route Middlewares
Lila includes several built-in middlewares that can be used as decorators on your routes:
-
@login_required: Validates an active session. If not found, redirects to a login page (default:/login). -
@session_active: Checks if a session is already active. If so, redirects to a dashboard (default:/dashboard). -
@validate_token: Validates a JWT token from theAuthorization: Bearer <token>header.
from lila.core.middleware import login_required, session_active, validate_token
# Require authentication for this route
@router.get('/dashboard')
@login_required
async def dashboard(request: Request):
return render(request=request, template='dashboard')
# Redirect authenticated users away from login page
@router.get('/login')
@session_active
async def login_page(request: Request):
return render(request=request, template='login')
# Validate JWT for API routes
@router.get('/api/data')
@validate_token
async def api_data(request: Request):
return JSONResponse({"data": "secure"})
️ Creating Custom Middlewares
You can easily create your own route middlewares using the create_decorator
helper. This tool automatically handles the boilerplate of Python decorators.
from lila.core.middleware import create_decorator
@create_decorator
async def check_admin(request: Request, role="admin"):
# If the logic returns a response, it breaks the chain and returns it
# If it returns True or None, it continues to the route
user = request.state.user
if user.get("role") != role:
return JSONResponse({"error": "Unauthorized"}, status_code=403)
return True
# Usage in routes:
# @check_admin -> uses default role="admin"
# @check_admin(role="superadmin") -> uses custom role
️ Router-Level Middlewares
Instead of decorating every route manually, you can assign one or more middlewares to a Router during initialization. Every route registered on that router will inherit them and run them in left-to-right order.
from lila.core.routing import Router
from lila.core.middleware import login_required, validate_token
# Both middlewares will automatically run for every route in this router
auth_router = Router(prefix="v1/api", middlewares=[login_required, validate_token])
@auth_router.get("/user")
async def user_details(request: Request):
return JSONResponse({"user": request.state.user})
CSRF Protection Middleware
Lila includes a built-in @csrf decorator that protects routes against Cross-Site Request Forgery attacks. It verifies the CSRF token on all unsafe HTTP methods (POST, PUT, PATCH, DELETE). Safe methods (GET, HEAD, OPTIONS) are always allowed through.
The token is read from the X-CSRF-Token header first, then from the csrf field in the request body, and compared against the signed _csrf cookie set by render(csrf=True).
from lila.core.templates import render
from lila.core.middleware import csrf
from lila.core.responses import JSONResponse
from lila.core.request import Request
# GET: render the form with CSRF token injected automatically
@router.get('/contact')
async def contact_page(request: Request):
return render(request, 'contact', csrf=True)
# POST: verify token before processing the form
@router.post('/contact')
@csrf
async def contact_submit(request: Request):
return JSONResponse({"success": True})
In your Jinja2 template, use the csrf_input global helper to render the hidden field:
<form method="POST" action="/contact">
{{ csrf_input | safe }}
<input type="text" name="name" />
<button type="submit">Send</button>
</form>
The Http() function in public/js/utils.js automatically detects document.getElementById('csrf') and sends the X-CSRF-Token header on every fetch request. No additional client-side code is required.
Global Security Middlewares
Lila includes a set of global HTTP middlewares for application-wide protection. These are configured in
main.py using the Middleware
class.
LoggingMiddleware: Automatically logs requests (ignoring static files).SecurityHeadersMiddleware: Adds security headers like X-XSS-Protection, X-Frame-Options, and the custom Powered-By: Lila Framework header. It also performs automatic XSS detection in query parameters using theSecurityclass.SecurityShieldMiddleware: Blocks malicious paths and IPs.RateLimitMiddleware: Protects against brute force and DoS attacks.ErrorHandlerMiddleware: Gracefully handles exceptions and Pydantic validation errors.
from lila.core.middleware import (
Middleware,
LoggingMiddleware,
SecurityHeadersMiddleware,
SecurityShieldMiddleware,
RateLimitMiddleware,
ErrorHandlerMiddleware,
)
middlewares = [
Middleware(LoggingMiddleware),
Middleware(SecurityHeadersMiddleware),
Middleware(SecurityShieldMiddleware),
Middleware(RateLimitMiddleware),
Middleware(ErrorHandlerMiddleware)
]
app = App(..., middleware=middlewares)