Built-in middleware
Built-in middleware lives in the Dumbo\Middleware namespace. Each one is a static factory that returns a middleware callable.
use Dumbo\Middleware\BasicAuth;
use Dumbo\Middleware\BearerAuth;
use Dumbo\Middleware\BodyLimit;
use Dumbo\Middleware\Cache;
use Dumbo\Middleware\Compress;
use Dumbo\Middleware\CORS;
use Dumbo\Middleware\Csrf;
use Dumbo\Middleware\Logger;
use Dumbo\Middleware\RequestId;
Basic auth
Protect routes with HTTP Basic authentication:
$app->use('/admin', BasicAuth::basicAuth('user:password'));
Allow multiple users, or verify credentials yourself:
$app->use('/admin', BasicAuth::basicAuth([
'users' => [
['username' => 'jamie', 'password' => 'secret'],
['username' => 'sam', 'password' => 'hunter2'],
],
'realm' => 'Admin',
]));
$app->use('/admin', BasicAuth::basicAuth([
'verifyUser' => fn($username, $password, $context) => checkLogin($username, $password),
]));
Bearer auth
Require a bearer token in the Authorization header:
$app->use('/api', BearerAuth::bearerAuth('secret-token'));
$app->use('/api', BearerAuth::bearerAuth([
'tokens' => ['token-one', 'token-two'],
]));
$app->use('/api', BearerAuth::bearerAuth([
'verifyToken' => fn($token, $context) => isValidToken($token),
]));
CORS
Add CORS headers and answer preflight requests:
$app->use(CORS::cors());
$app->use('/api', CORS::cors([
'origin' => ['https://example.com', 'https://app.example.com'],
'allowMethods' => ['GET', 'POST'],
'allowHeaders' => ['Content-Type', 'Authorization'],
'exposeHeaders' => ['X-Request-Id'],
'credentials' => true,
'maxAge' => 600,
]));
origin may be "*", a single origin, an array of origins, or a callable receiving ($origin, $context) that returns the origin to allow, or null to deny.
CSRF
Issue a token on safe requests and check it on every other request. You decide where the token is stored:
use Dumbo\Helpers\Cookie;
$app->use(Csrf::csrf([
'getToken' => fn($context) => Cookie::get($context, 'csrf_token'),
'setToken' => fn($context, $token) => Cookie::set($context, 'csrf_token', $token, [
'httpOnly' => true,
'sameSite' => Cookie::SAME_SITE_STRICT,
]),
]));
$app->get('/form', function ($context) {
$token = $context->get('csrf_token');
return $context->html("<form method='post'><input type='hidden' name='csrf_token' value='$token'>...</form>");
});
| Option | Default | Description |
|---|---|---|
getToken |
required | Reads the stored token |
setToken |
required | Stores a new token |
tokenName |
csrf_token |
Body field and context variable name |
headerName |
X-CSRF-TOKEN |
Header to send and read the token in |
useHeader |
false |
Read the token from the header instead of the body |
tokenLength |
32 |
Length of generated tokens |
errorHandler |
null |
Returns a response for invalid tokens |
Body limit
Reject requests whose Content-Length is larger than a number of bytes with a 413:
$app->use(BodyLimit::bodyLimit(1024 * 1024));
$app->use('/upload', BodyLimit::bodyLimit(10 * 1024 * 1024, function ($context, $maxSize) {
return $context->json(['error' => "Max upload size is $maxSize bytes"], 413);
}));
Cache
Add Cache-Control and ETag headers to successful GET and HEAD responses, and respond 304 Not Modified when the client already has them:
$app->use('/blog', Cache::cache([
'type' => 'public', // default "private"
'maxAge' => 3600, // default 86400
'mustRevalidate' => true,
]));
Compress
Compress responses with gzip or deflate when the client accepts it:
$app->use(Compress::compress());
$app->use(Compress::compress([
'threshold' => 2048, // Minimum body size in bytes, default 1024
'encoding' => 'gzip', // Force an encoding
]));
Logger
Log each request, its status and how long it took to any PSR-3 logger:
$app->use(Logger::logger($psrLogger));
// --> GET /users
// <-- GET /users 200 3.42ms
Request ID
Reuse the incoming X-Request-Id header or generate one, store it as the requestId context variable and echo it on the response:
$app->use(RequestId::requestId());
$app->get('/', fn($context) => $context->text($context->get('requestId')));
$app->use(RequestId::requestId([
'headerName' => 'X-Trace-Id',
'limitLength' => 64,
'generator' => fn($context) => uniqid('req_'),
]));