Method: POST
URI: https://api.example.com/users
Header: application/json
Body: {"name":"Alice","email":"[email protected]"}
Status: 201 Created
Location: /users/42
Body: {"id":42,"name":"Alice","created":true}
Original status: 201
Updated status: 200
<?php
use Psr\Http\Message\{ServerRequestInterface, ResponseInterface};
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};
// Authentication middleware
class AuthMiddleware implements MiddlewareInterface {
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
$authHeader = $request->getHeaderLine('Authorization');
if (!str_starts_with($authHeader, 'Bearer ')) {
return (new \Nyholm\Psr7\Response(401))
->withHeader('Content-Type', 'application/json')
->withBody((new \Nyholm\Psr7\Factory\Psr17Factory())
->createStream(json_encode(['error' => 'Unauthorized'])));
}
$token = substr($authHeader, 7);
$request = $request->withAttribute('user_token', $token);
return $handler->handle($request);
}
}
// Logging middleware
class LoggingMiddleware implements MiddlewareInterface {
private array $log = [];
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
$start = microtime(true);
$response = $handler->handle($request);
$elapsed = round((microtime(true) - $start) * 1000, 2);
$this->log[] = sprintf(
"%s %s → %d (%sms)",
$request->getMethod(),
$request->getUri()->getPath(),
$response->getStatusCode(),
$elapsed
);
return $response->withHeader('X-Response-Time', $elapsed . 'ms');
}
public function getLog(): array { return $this->log; }
}
// CORS middleware
class CorsMiddleware implements MiddlewareInterface {
public function __construct(private array $allowedOrigins = ['*']) {}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface {
$response = $handler->handle($request);
return $response
->withHeader('Access-Control-Allow-Origin', implode(', ', $this->allowedOrigins))
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->withHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
}
}
echo "Middleware classes defined: AuthMiddleware, LoggingMiddleware, CorsMiddleware\n";
<?php
use Psr\Http\Message\{ServerRequestInterface, ResponseInterface};
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};
class MiddlewarePipeline implements RequestHandlerInterface {
private array $middleware;
public function __construct(
private RequestHandlerInterface $finalHandler,
MiddlewareInterface ...$middleware
) {
$this->middleware = array_reverse($middleware);
}
public function handle(ServerRequestInterface $request): ResponseInterface {
$handler = $this->finalHandler;
foreach ($this->middleware as $mw) {
$handler = new class($mw, $handler) implements RequestHandlerInterface {
public function __construct(
private MiddlewareInterface $mw,
private RequestHandlerInterface $next
) {}
public function handle(ServerRequestInterface $request): ResponseInterface {
return $this->mw->process($request, $this->next);
}
};
}
return $handler->handle($request);
}
}
// Final handler (the actual application logic)
$appHandler = new class implements RequestHandlerInterface {
public function handle(ServerRequestInterface $request): ResponseInterface {
$factory = new \Nyholm\Psr7\Factory\Psr17Factory();
$token = $request->getAttribute('user_token', 'none');
$path = $request->getUri()->getPath();
$data = match(true) {
$path === '/api/users' => ['users' => [['id' => 1, 'name' => 'Alice']]],
str_starts_with($path, '/api/users/') => ['user' => ['id' => (int)substr($path, 11), 'name' => 'User']],
default => ['error' => 'Not found'],
};
$status = isset($data['error']) ? 404 : 200;
return $factory->createResponse($status)
->withHeader('Content-Type', 'application/json')
->withBody($factory->createStream(json_encode($data)));
}
};
// Build pipeline
$logging = new LoggingMiddleware();
$pipeline = new MiddlewarePipeline(
$appHandler,
new CorsMiddleware(['https://app.example.com']),
$logging,
new AuthMiddleware(),
);
$factory = new \Nyholm\Psr7\Factory\Psr17Factory();
// Test request 1: authorized
$req1 = $factory->createServerRequest('GET', 'https://api.example.com/api/users')
->withHeader('Authorization', 'Bearer valid-token-abc');
$res1 = $pipeline->handle($req1);
echo "GET /api/users: " . $res1->getStatusCode() . " " . $res1->getBody() . "\n";
echo " CORS: " . $res1->getHeaderLine('Access-Control-Allow-Origin') . "\n";
// Test request 2: unauthorized
$req2 = $factory->createServerRequest('GET', 'https://api.example.com/api/users');
$res2 = $pipeline->handle($req2);
echo "\nGET /api/users (no auth): " . $res2->getStatusCode() . " " . $res2->getBody() . "\n";
// Check logs
echo "\nRequest log:\n";
foreach ($logging->getLog() as $entry) {
echo " $entry\n";
}
GET /api/users: 200 {"users":[{"id":1,"name":"Alice"}]}
CORS: https://app.example.com
GET /api/users (no auth): 401 {"error":"Unauthorized"}
Request log:
GET /api/users → 200 (0.15ms)
GET /api/users → 401 (0.08ms)
GET /api/products → 200: [{"id":1,"name":"Widget","price":9.99},{"id":2,"na
GET /api/products/2 → 200: {"id":2,"name":"Gadget","price":24.99}
GET /api/products/99 → 200: {"error":"Product not found"}
GET /api/products → 401: {"error":"Unauthorized"}
Log:
GET /api/products → 200 (0.23ms)
GET /api/products/2 → 200 (0.11ms)
GET /api/products/99 → 200 (0.12ms)
GET /api/products → 401 (0.09ms)