Middleware in InspireCMS
Middleware provides a mechanism for filtering HTTP requests entering your application. For example, middleware can be used for authentication, localization, content caching, or response modification. This guide explains how to create and use custom middleware in InspireCMS.
Understanding Middleware in InspireCMS
InspireCMS uses Laravel's middleware architecture, which allows code to be executed before and after an HTTP request is processed. There are several types of middleware in InspireCMS:
- Global Middleware: Applied to every HTTP request
- Route Middleware: Applied to specific routes or route groups
- Frontend Middleware: Applied to frontend content routes
- Admin Panel Middleware: Applied specifically to routes within the InspireCMS admin panel
Creating Custom Middleware
Step 1: Generate a Middleware Class
You can create a middleware class using Laravel's artisan command:
php artisan make:middleware YourMiddlewareName
This creates a new middleware class in the app/Http/Middleware directory.
Step 2: Implement the Middleware Logic
Edit the generated file to implement your middleware logic:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class YourMiddlewareName
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
// Code executed before the request is processed
// Check for certain conditions
if ($someCondition) {
// You can redirect, abort, or modify the request
return redirect('/somewhere-else');
}
// Process the request
$response = $next($request);
// Code executed after the request is processed
// You can modify the response
$response->header('X-Custom-Header', 'Value');
return $response;
}
}
Registering Custom Middleware
There are several ways to register your middleware with InspireCMS:
1. Global Middleware
To apply middleware to every HTTP request, register it in the $middleware property of the app/Http/Kernel.php file:
protected $middleware = [
// Other middleware...
\App\Http\Middleware\YourMiddlewareName::class,
];
2. Route Middleware
To make your middleware available to specific routes, register it in the $routeMiddleware property of the app/Http/Kernel.php file:
protected $routeMiddleware = [
// Other middleware...
'your-middleware' => \App\Http\Middleware\YourMiddlewareName::class,
];
You can then apply it to routes in a route file:
Route::get('/your-route', function () {
// Your route logic
})->middleware('your-middleware');
3. Frontend Middleware
To apply middleware specifically to frontend content routes in InspireCMS, modify the configuration:
// config/inspirecms.php
'frontend' => [
'routes' => [
'middleware' => [
// Add your middleware here
'your-middleware',
],
],
],
4. Admin Panel Middleware
To apply middleware specifically to the admin panel in InspireCMS, you need to create a custom panel provider. This allows you to define middleware that will only run for admin panel routes.
First, make sure you've set up a custom panel provider as described in Extending the Admin Panel.
Once you have your custom panel provider set up, you can add middleware to the admin panel:
<?php
namespace App\Providers;
use Filament\Panel;
use SolutionForest\InspireCms\CmsPanelProvider;
class MyCmsPanelProvider extends CmsPanelProvider
{
protected function configureCmsPanel(Panel $panel): Panel
{
$panel = parent::configureCmsPanel($panel);
return $panel
// Add middleware that runs on all admin panel routes
->middleware([
\App\Http\Middleware\AdminLoggingMiddleware::class,
\App\Http\Middleware\AdminAnalyticsMiddleware::class,
])
// Add middleware that runs only on authenticated admin panel routes
->authMiddleware([
\App\Http\Middleware\AdminPermissionsMiddleware::class,
]);
}
}
For example, you might create a middleware to log all admin actions:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class AdminLoggingMiddleware
{
public function handle(Request $request, Closure $next)
{
// Log the admin action
Log::channel('admin')->info('Admin action', [
'user' => auth()->user()?->name ?? 'Guest',
'url' => $request->fullUrl(),
'method' => $request->method(),
'ip' => $request->ip(),
]);
return $next($request);
}
}
Or middleware to implement additional admin permissions:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Filament\Notifications\Notification;
class AdminPermissionsMiddleware
{
public function handle(Request $request, Closure $next)
{
// Get the authenticated user
$user = auth()->user();
// Example: Check if the user has a specific permission for certain routes
if ($request->is('admin/settings*') && !$user->can('manage_settings')) {
// Show notification and redirect
Notification::make()
->title('Access denied')
->body('You do not have permission to manage settings.')
->danger()
->send();
return redirect()->route('filament.admin.pages.dashboard');
}
return $next($request);
}
}
Common Use Cases for Middleware
1. Site Maintenance Mode
Create middleware that checks if the site is in maintenance mode:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Cache;
class CheckMaintenanceMode
{
public function handle($request, Closure $next)
{
if (Cache::get('site_maintenance_mode') && !$this->isAdmin($request)) {
return response()->view('maintenance', [], 503);
}
return $next($request);
}
protected function isAdmin($request)
{
return $request->is('cms/*') && auth()->guard('inspirecms')->check();
}
}
2. Custom Analytics Tracking
Middleware to add analytics tracking to all pages:
<?php
namespace App\Http\Middleware;
use Closure;
class TrackPageViews
{
public function handle($request, Closure $next)
{
$response = $next($request);
// Only process HTML responses
if (!$request->ajax() &&
$response->headers->get('Content-Type') === 'text/html; charset=UTF-8') {
$content = $response->getContent();
// Add analytics code before </body>
$analyticsCode = "<script>// Your analytics code here</script>";
$content = str_replace('</body>', $analyticsCode . '</body>', $content);
$response->setContent($content);
}
return $response;
}
}
3. Language Selection
Middleware to set the site language based on user preference:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Session;
class LocaleMiddleware
{
public function handle($request, Closure $next)
{
// Check for language in URL (e.g., /en/page)
$segments = $request->segments();
$locale = '';
if (count($segments) > 0 && in_array($segments[0], ['en', 'fr', 'de', 'es'])) {
$locale = $segments[0];
Session::put('locale', $locale);
}
// If no locale in URL, check session or cookie
if (empty($locale)) {
$locale = Session::get('locale', config('app.locale'));
}
// Set the application locale
App::setLocale($locale);
return $next($request);
}
}
4. Content Security Policy
Add security headers to protect against XSS and other attacks:
<?php
namespace App\Http\Middleware;
use Closure;
class ContentSecurityPolicy
{
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data:;");
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
return $response;
}
}
5. Cache Control
Middleware to manage cache headers:
<?php
namespace App\Http\Middleware;
use Closure;
class CacheControl
{
public function handle($request, Closure $next)
{
$response = $next($request);
// Check if the response is cacheable
if (!$request->is('cms/*') && $request->isMethod('GET')) {
$response->headers->set('Cache-Control', 'public, max-age=3600'); // 1 hour
} else {
// Don't cache admin pages or non-GET requests
$response->headers->set('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0');
$response->headers->set('Pragma', 'no-cache');
$response->headers->set('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT');
}
return $response;
}
}
Middleware Groups
You can organize related middleware into groups for easier application to routes. Define middleware groups in app/Http/Kernel.php:
protected $middlewareGroups = [
'web' => [
// Laravel's default web middleware...
],
'api' => [
// Laravel's default API middleware...
],
'inspirecms.frontend' => [
\App\Http\Middleware\TrackPageViews::class,
\App\Http\Middleware\CacheControl::class,
\App\Http\Middleware\LocaleMiddleware::class,
],
];
Then apply the group to routes:
Route::middleware('inspirecms.frontend')->group(function () {
// Routes here will have all the middleware in the group applied
});
Middleware Priority
The order in which middleware is executed matters. You can set the priority in app/Http/Kernel.php:
protected $middlewarePriority = [
// High priority (executed first)
\App\Http\Middleware\CheckMaintenanceMode::class,
\Illuminate\Session\Middleware\StartSession::class,
// Medium priority
\App\Http\Middleware\LocaleMiddleware::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
// Low priority (executed last)
\App\Http\Middleware\TrackPageViews::class,
\App\Http\Middleware\CacheControl::class,
];
Best Practices
- Keep middleware focused: Each middleware should have a single responsibility
- Use middleware parameters when configurability is needed
- Consider performance impact, especially for middleware applied globally
- Document your middleware to explain its purpose and behavior
- Test thoroughly, especially edge cases
- Check for existing middleware before creating a new one—Laravel and InspireCMS already include many useful middleware classes
By following these guidelines, you can effectively extend InspireCMS's request handling capability through custom middleware.