Middleware
Introductionâ
Middleware runs before the controller and can stop the request. Use it for the checks that repeat across routes â authentication, capability checks, nonce verification.
Writing middlewareâ
A middleware is a class in backend/app/HTTP/Middleware with a handle() method. Return true to let the request through, or a Response to stop it:
namespace YourPlugin\HTTP\Middleware;
if (!defined('ABSPATH')) {
exit;
}
use YourPlugin\Deps\BitApps\WPKit\Http\Response;
use YourPlugin\Deps\BitApps\WPKit\Utils\Capabilities;
final class AdminCheckerMiddleware
{
public function handle()
{
if (!Capabilities::check('manage_options')) {
return Response::error('Access Denied: Only administrators are allowed to make this request')
->httpStatus(411);
}
return true;
}
}
Anything other than true is treated as the response and sent immediately â the controller never runs.
handle() can type-hint the request, which the router injects the same way it does for controllers:
final class NonceCheckerMiddleware
{
public function handle(Request $request)
{
if (!$request->has('_ajax_nonce')
|| !wp_verify_nonce(sanitize_key($request->_ajax_nonce), Config::withPrefix('nonce'))
) {
return Response::error('Invalid nonce token')->httpStatus(411);
}
return true;
}
}
Registering middlewareâ
Routes refer to middleware by short name. Map the names to classes in Plugin::middlewares():
public function middlewares()
{
return [
'nonce' => NonceCheckerMiddleware::class,
'isAdmin' => AdminCheckerMiddleware::class,
];
}
A route that names an unregistered middleware is not an error â the router finds nothing to run and the request proceeds. A route asking for isAdmin against an empty registry answers as though it had no protection at all, so keep the registry in sync when you add middleware.
Attaching middlewareâ
Attach by name to a single route or a group:
Route::post('flows/save', [FlowController::class, 'store'])->middleware('nonce');
Route::group(
function (): void {
// every route in here runs both
}
)->middleware('nonce', 'isAdmin');
Group and route middleware merge, and run in the order they were added â the group's first.
Middleware parametersâ
Pass parameters after a colon; multiple parameters are comma-separated. They arrive as arguments to handle().
Given a middleware of your own registered as can:
public function handle($capability, $fallback = null)
{
return Capabilities::check($capability) ?: Response::error('Access Denied')->httpStatus(411);
}
a route selects the capability at the call site:
Route::get('flows', [FlowController::class, 'index'])->middleware('can:manage_options,edit_posts');
nonce and isAdmin are the two the boilerplate ships. can above is an example of one you would write and register yourself.