您可以在 Slim 应用程序之前和之后运行代码,以按照您认为合适的方式操作 Request 和 Response 对象。这称为中间件。你为什么想做这个?也许您想保护您的应用程序免受跨站点请求伪造。也许您想在应用运行之前对请求进行身份验证。中间件非常适合这些场景。
中间件实现PSR-15 中间件接口:
Psr\Http\Message\ServerRequestInterface
- PSR-7 请求对象Psr\Http\Server\RequestHandlerInterface
- PSR-15 请求处理程序对象它可以对这些对象做任何合适的事情。唯一的硬性要求是中间件必须返回一个 Psr\Http\Message\ResponseInterface
. 每个中间件应该调用下一个中间件并将 Request 对象作为参数传递给它。
不同的框架使用不同的中间件。Slim 添加中间件作为核心应用程序周围的同心层。每个新的中间件层都围绕着任何现有的中间件层。随着额外的中间件层的添加,同心结构向外扩展。
最后添加的中间件层最先被执行。
当你运行 Slim 应用程序时,Request 对象从外向内遍历中间件结构。它们首先进入最外层的中间件,然后是下一个最外层的中间件,(依此类推),直到最终到达 Slim 应用程序本身。在 Slim 应用程序调度适当的路由后,生成的 Response 对象退出 Slim 应用程序并从内到外遍历中间件结构。最终,最终的 Response 对象退出最外层的中间件,被序列化为原始 HTTP 响应,并返回给 HTTP 客户端。这是一个说明中间件流程的图表:
中间件是一个接受两个参数的可调用对象:一个Request
对象和一个RequestHandler
对象。每个中间件必须返回一个 Psr\Http\Message\ResponseInterface
.
这个示例中间件是一个闭包。
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Factory\AppFactory;
use Slim\Psr7\Response;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
/**
* Example middleware closure
*
* @param ServerRequest $request PSR-7 request
* @param RequestHandler $handler PSR-15 request handler
*
* @return Response
*/
$beforeMiddleware = function (Request $request, RequestHandler $handler) {
$response = $handler->handle($request);
$existingContent = (string) $response->getBody();
$response = new Response();
$response->getBody()->write('BEFORE' . $existingContent);
return $response;
};
$afterMiddleware = function ($request, $handler) {
$response = $handler->handle($request);
$response->getBody()->write('AFTER');
return $response;
};
$app->add($beforeMiddleware);
$app->add($afterMiddleware);
// ...
$app->run();
这个示例中间件是一个实现魔术__invoke()
方法的可调用类。
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Psr7\Response;
class ExampleBeforeMiddleware
{
/**
* Example middleware invokable class
*
* @param ServerRequest $request PSR-7 request
* @param RequestHandler $handler PSR-15 request handler
*
* @return Response
*/
public function __invoke(Request $request, RequestHandler $handler): Response
{
$response = $handler->handle($request);
$existingContent = (string) $response->getBody();
$response = new Response();
$response->getBody()->write('BEFORE' . $existingContent);
return $response;
}
}
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
class ExampleAfterMiddleware
{
/**
* Example middleware invokable class
*
* @param ServerRequest $request PSR-7 request
* @param RequestHandler $handler PSR-15 request handler
*
* @return Response
*/
public function __invoke(Request $request, RequestHandler $handler): Response
{
$response = $handler->handle($request);
$response->getBody()->write('AFTER');
return $response;
}
}
要将这些类用作中间件,您可以使用add(new ExampleMiddleware()); $app路由映射方法get()、post()、put()、patch()、delete()、options()、any() 或group()之后的函数链,如下面的代码所示。
<?php
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
// Add Middleware On App
$app->add(new ExampleMiddleware());
// Add Middleware On Route
$app->get('/', function () { ... })->add(new ExampleMiddleware());
// Add Middleware On Group
$app->group('/', function () { ... })->add(new ExampleMiddleware());
// ...
$app->run();
您可以将中间件添加到 Slim 应用程序、单个 Slim 应用程序路由或路由组。所有场景都接受相同的中间件并实现相同的中间件接口。
为每个传入的HTTP 请求调用应用程序中间件。使用 Slim 应用程序实例的add()方法添加应用程序中间件。本示例添加了上面的 Closure 中间件示例:
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Factory\AppFactory;
use Slim\Psr7\Response;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->add(function (Request $request, RequestHandler $handler) {
$response = $handler->handle($request);
$existingContent = (string) $response->getBody();
$response = new Response();
$response->getBody()->write('BEFORE ' . $existingContent);
return $response;
});
$app->add(function (Request $request, RequestHandler $handler) {
$response = $handler->handle($request);
$response->getBody()->write(' AFTER');
return $response;
});
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write('Hello World');
return $response;
});
$app->run();
这将输出此 HTTP 响应正文:
BEFORE Hello World AFTER
仅当其路由与当前 HTTP 请求方法和 URI 匹配时,路由中间件才会被调用。路由中间件在您调用任何 Slim 应用程序的路由方法(例如get()或post())后立即指定。每个路由方法都返回一个\Slim\Route实例,这个类提供与 Slim 应用程序实例相同的中间件接口。使用 Route 实例的add()方法将中间件添加到 Route 。本示例添加了上面的 Closure 中间件示例:
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$mw = function (Request $request, RequestHandler $handler) {
$response = $handler->handle($request);
$response->getBody()->write('World');
return $response;
};
$app->get('/', function (Request $request, Response $response, $args) {
$response->getBody()->write('Hello ');
return $response;
})->add($mw);
$app->run();
这将输出此 HTTP 响应正文:
Hello World
除了整个应用程序和能够接受中间件的标准路由之外,group()多路由定义功能还允许在内部使用单个路由。仅当路由组中间件的路由与组中已定义的 HTTP 请求方法和 URI 之一匹配时,才会调用路由组中间件。在回调中添加中间件,并通过在group()方法之后链接add()来设置整个组中间件。
示例应用程序,在一组 url 处理程序上使用回调中间件
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteCollectorProxy;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
$app->get('/', function (Request $request, Response $response) {
$response->getBody()->write('Hello World');
return $response;
});
$app->group('/utils', function (RouteCollectorProxy $group) {
$group->get('/date', function (Request $request, Response $response) {
$response->getBody()->write(date('Y-m-d H:i:s'));
return $response;
});
$group->get('/time', function (Request $request, Response $response) {
$response->getBody()->write((string)time());
return $response;
});
})->add(function (Request $request, RequestHandler $handler) use ($app) {
$response = $handler->handle($request);
$dateOrTime = (string) $response->getBody();
$response = $app->getResponseFactory()->createResponse();
$response->getBody()->write('It is now ' . $dateOrTime . '. Enjoy!');
return $response;
});
$app->run();
当调用/utils/date方法时,这将输出类似于下面的字符串
It is now 2015-07-06 03:11:01. Enjoy!
访问/utils/time会输出类似于下面的字符串
It is now 1436148762. Enjoy!
但是访问/ (domain-root),预计会生成以下输出,因为没有分配中间件
Hello World
从中间件传递属性的最简单方法是使用请求的属性。
在中间件中设置变量:
$request = $request->withAttribute('foo', 'bar');
在路由回调中获取变量:
$foo = $request->getAttribute('foo');
您可能会发现已经编写的 PSR-15 中间件类可以满足您的需求。这里有一些非官方的搜索列表。