中间件
您可以在 Slim 应用程序中运行代码,以便在所需时操纵请求和响应对象。这被称为中间件。为什么要这样做呢?也许您希望保护应用程序免受跨站请求伪造的攻击。或者您希望在应用程序运行之前对请求进行身份验证。对于这些情况,中间件非常适用。
什么是中间件?
中间件实现了 PSR-15 中间件接口:
Psr\Http\Message\ServerRequestInterface- PSR-7 请求对象Psr\Http\Server\RequestHandlerInterface- PSR-15 请求处理器对象
它可以根据需要对这些对象执行任何适当的操作。唯一的硬性要求是中间件 必须 返回一个 Psr\Http\Message\ResponseInterface 实例。每个中间件 应该 调用下一个中间件,并将请求对象作为参数传递给它。
中间件如何工作?
不同的框架使用中间件的方式不同。Slim 将中间件添加为围绕核心应用程序的同心圆层。每个新的中间件层都环绕着任何现有的中间件层。随着添加额外的中间件层,同心结构向外扩展。
最后添加的中间件层将首先被执行。
当您运行 Slim 应用程序时,请求对象从外向内遍历中间件结构。它们首先进入最外层的中间件,然后进入下一个最外层中间件(以此类推),直到最终到达 Slim 应用程序本身。在 Slim 应用程序调度适当的路由之后,生成的响应对象离开 Slim 应用程序并从内向外遍历中间件结构。最终,最外层的中间件将输出最终的响应对象,并将其序列化为原始 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();
/**
* 示例中间件闭包
*
* @param ServerRequest $request PSR-7 请求
* @param RequestHandler $handler PSR-15 请求处理器
*
* @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
{
/**
* 示例中间件可调用类
*
* @param ServerRequest $request PSR-7 请求
* @param RequestHandler $handler PSR-15 请求处理器
*
* @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
{
/**
* 示例中间件可调用类
*
* @param ServerRequest $request PSR-7 请求
* @param RequestHandler $handler PSR-15 请求处理器
*
* @return Response
*/
public function __invoke(Request $request, RequestHandler $handler): Response
{
$response = $handler->handle($request);
$response->getBody()->write('AFTER');
return $response;
}
}
要将这些类作为中间件使用,可以在 $app 的路由映射方法 get()、post()、put()、patch()、delete()、options()、any() 或 group() 之后链式调用 add(new ExampleMiddleware()); 函数,如下面的代码所示。
<?php
use Slim\Factory\AppFactory;
require __DIR__ . '/../vendor/autoload.php';
$app = AppFactory::create();
// 在应用程序上添加中间件
$app->add(new ExampleMiddleware());
// 在路由上添加中间件
$app->get('/', function () { ... })->add(new ExampleMiddleware());
// 在分组上添加中间件
$app->group('/', function () { ... })->add(new ExampleMiddleware());
// ...
$app->run();
如何添加中间件?
您可以向 Slim 应用程序、单个 Slim 应用程序路由或路由组添加中间件。所有的情况都接受相同的中间件,并实现相同的中间件接口。
应用程序中间件
应用程序中间件对于每个 传入的 HTTP 请求都会被调用。使用 Slim 应用程序实例的 add() 方法添加应用程序中间件。下面的示例添加了上述闭包中间件:
<?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 应用程序实例相同的中间件接口。使用路由实例的 add() 方法将中间件添加到路由。下面的示例添加了上述闭包中间件:
<?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 方法时,将输出类似于以下内容的字符串
现在是 2015-07-06 03:11:01。尽情享受吧!
访问 /utils/time 将输出类似于以下内容的字符串
现在是 1436148762。尽情享受吧!
但是访问 /(域根)将生成以下输出,因为没有分配中间件
Hello World
从中间件传递变量
从中间件传递属性最简单的方法是使用请求的属性。
在中间件中设置变量:
$request = $request->withAttribute('foo', 'bar');
在路由回调函数中获取变量:
$foo = $request->getAttribute('foo');
查找可用的中间件
您可以找到已经编写好以满足您需求的 PSR-15 中间件类。以下是一些非官方的列表供搜索。