# 中间件
*****
您可以在Slim应用程序 **before** 和 **after** 运行代码,以根据需要操作请求和响应对象。这称为中间件。你为什么要这么做?也许你想保护你的应用程序不被跨站请求伪造。也许您想在应用程序运行之前验证请求。中间件非常适合这些场景。
You can run code*before*and*after*your Slim application to manipulate the Request and Response objects as you see fit. This is called*middleware*. Why would you want to do this? Perhaps you want to protect your app from cross-site request forgery. Maybe you want to authenticate requests before your app runs. Middleware is perfect for these scenarios.
## 什么是中间件?
中间件实现了 [PSR-15 Middleware Interface](https://www.php-fig.org/psr/psr-15/):
1. `Psr\Http\Message\ServerRequestInterface`\- The PSR-7 request object
2. `Psr\Http\Server\RequestHandlerInterface`\- The PSR-15 request handler object
它可以对这些对象做任何适当的事情。唯一的硬性要求是中间件**必须**返回一个`Psr\Http\Message\ResponseInterface`实例。每个中间件都**应该**调用下一个中间件,并将请求和响应对象作为参数传递给它。
It can do whatever is appropriate with these objects. The only hard requirement is that a middleware**MUST**return an instance of`Psr\Http\Message\ResponseInterface`. Each middleware**SHOULD**invoke the next middleware and pass it Request and Response objects as arguments.
## 中间件如何工作?
Different frameworks use middleware differently. Slim adds middleware as concentric layers surrounding your core application. Each new middleware layer surrounds any existing middleware layers. The concentric structure expands outwardly as additional middleware layers are added.
> 不同的框架以不同的方式使用中间件。Slim将中间件作为围绕核心应用程序的同心层添加。每个新的中间件层都围绕着任何现有的中间件层。随着附加的中间件层的添加,同心结构向外扩展。
The last middleware layer added is the first to be executed.
> 最后添加的中间件层是要执行的第一个层。
When you run the Slim application, the Request object traverses the middleware structure from the outside in. They first enter the outer-most middleware, then the next outer-most middleware, (and so on), until they ultimately arrive at the Slim application itself. After the Slim application dispatches the appropriate route, the resultant Response object exits the Slim application and traverses the middleware structure from the inside out. Ultimately, a final Response object exits the outer-most middleware, is serialized into a raw HTTP response, and is returned to the HTTP client. Here’s a diagram that illustrates the middleware process flow:
> 当您运行Slim应用程序时,请求对象从外向内遍历中间件结构。它们首先进入最外面的中间件,然后进入下一个最外面的中间件(等等),直到最终到达Slim应用程序本身。Slim应用程序分派适当的路由后,生成的响应对象退出Slim应用程序并从内到外遍历中间件结构。最终,最终的响应对象退出最外层的中间件,序列化为原始的HTTP响应,然后返回给HTTP客户端。下图展示了中间件流程流:
![](https://box.kancloud.cn/2015-10-23_5629d1b6da0db.png)
## 如何编写中间件?
Middleware is a callable that accepts two arguments: a`Request`object and a`RequestHandler`object. Each middleware**MUST**return an instance of`Psr\Http\Message\ResponseInterface`.
> 中间件是一个可调用的,它接受两个参数:
>
> 一个`Request`对象和一个`RequestHandler`对象。
>
> 每个中间件**必须**返回一个`Psr\Http\Message\ResponseInterface`的实例。
## 关闭中间件的例子。
> 这个示例中间件是一个闭包。
~~~php
<?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();
~~~
## 调用类中间件的例子
This example middleware is an invokable class that implements the magic`__invoke()`method.
> 这个示例中间件是一个可调用的类,它实现了神奇的 **invoke()** 方法。
~~~php
<?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
<?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;
}
}
~~~
To use these classes as a middleware, you can use**add(new ExampleMiddleware());**function chain after the**$app**route mapping methods**get(), post(), put(), patch(), delete(), options(), any()**or**group()**, which in the code below, any one of these, could represent $subject.
> 要将这些类用作中间件,可以使用`add(new ExampleMiddleware())`;
>
> $app路由映射方法`get()`、`post()`、`put()`、`patch()`、`delete()`、`options()`、`any()`或`group()`之后的函数链,在下面的代码中,这些方法中的任何一个都可以表示`$subject`。
~~~php
<?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();
~~~
## 如何添加中间件?
You may add middleware to a Slim application, to an individual Slim application route or to a route group. All scenarios accept the same middleware and implement the same middleware interface.
> 可以向Slim应用程序、单个Slim应用程序路由或路由组添加中间件。
>
> 所有场景都接受相同的中间件并实现相同的中间件接口。
## 应用程序中间件
Application middleware is invoked for every**incoming**HTTP request. Add application middleware with the Slim application instance’s **add()** method. This example adds the Closure middleware example above:
> 为每个**传入**的HTTP请求调用应用程序中间件。
>
> 使用Slim应用程序实例的`add()`方法的应用程序中间件。
>
> 这个例子添加了上面的闭包中间件例子:
~~~php
<?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响应体:
```
~~~bash
BEFORE Hello World AFTER
~~~
## 路由中间件
Route middleware is invoked*only if*its route matches the current HTTP request method and URI. Route middleware is specified immediately after you invoke any of the Slim application’s routing methods (e.g.,**get()**or**post()**). Each routing method returns an instance of **\Slim\Route** , and this class provides the same middleware interface as the Slim application instance. Add middleware to a Route with the Route instance’s **add()** method. This example adds the Closure middleware example above:
> 路由中间件 *只有* 在其路由与当前HTTP请求方法和URI匹配时才会被调用。
>
> 路由中间件是在您调用任何Slim应用程序的路由方法(例如,**get()**或**post()**)后立即指定的。
>
> 每个路由方法返回一个`\Slim\Route`实例,该类提供与Slim应用程序实例相同的中间件接口。
>
> 使用路由实例的 **Add()** 方法向路由添加中间件。
>
> 这个例子添加了上面的闭包中间件例子:
~~~php
<?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响应体:
~~~bash
Hello World
~~~
## 组中间件
In addition to the overall application, and standard routes being able to accept middleware, the **group()** multi-route definition functionality, also allows individual routes internally. Route group middleware is invoked*only if*its route matches one of the defined HTTP request methods and URIs from the group. To add middleware within the callback, and entire-group middleware to be set by chaining **add()** after the **group()** method.
> 除了整个应用程序和能够接受中间件的标准路由之外, **group()** 多路由定义功能还允许内部使用单个路由。
>
> *只有*当路由组中间件的路由与组中定义的HTTP请求方法和uri之一匹配时,才会调用路由组中间件。
>
> 在回调中添加中间件,并在 **group()** 方法之后通过链接 **add()** 设置实体组中间件。
Sample Application, making use of callback middleware on a group of url-handlers
> 示例应用程序,利用一组url处理程序上的回调中间件
~~~php
<?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) {
return $response->getBody()->write('Hello World');
});
$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`方法时,将输出类似于下面的字符串
~~~bash
It is now 2015-07-06 03:11:01. Enjoy!
~~~
访问`/utils/time`将输出类似于下面的字符串
~~~bash
It is now 1436148762. Enjoy!
~~~
But visiting `/` (domain-root) , would be expected to generate the following output as no middleware has been assigned
但是,由于没有分配任何中间件,因此访问`/`(域-根)将生成以下输出
~~~bash
Hello World
~~~
## 从中间件传递变量
The easiest way to pass attributes from middleware is to use the request’s attributes.
> 从中间件传递属性的最简单方法是使用请求的属性。
Setting the variable in the middleware:
> 在中间件中设置变量:
~~~php
$request = $request->withAttribute('foo', 'bar');
~~~
获取路由回调中的变量:
~~~php
$foo = $request->getAttribute('foo');
~~~
## 寻找可用的中间件
> 您可能会发现已经编写了满足您需要的PSR-15中间件类。以下是一些可以搜索的非官方列表。
* [Middleware for Slim Framework v4.x wiki](https://github.com/slimphp/Slim/wiki/Middleware-for-Slim-Framework-v4.x)
* [middlewares/awesome-psr15-middlewares](https://github.com/middlewares/awesome-psr15-middlewares)
- 开始
- 安装
- 升级指南
- Web服务器
- 概念
- 生命周期
- PSR 7
- 中间件
- 依赖容器
- 实例 及通知和警告处理
- Request
- 请求方法
- 请求头信息
- 请求主体
- 上传的文件
- 请求帮助
- 路由对象
- Response
- 响应状态
- 响应标头
- 响应体
- 返回JSON
- 视图模板
- 路由
- 创建路由
- 路由回调
- 路由策略
- 路线占位符
- 路由名
- 路由组
- 路由中间件
- 路由表达式缓存
- 容器识别解析
- 封装中间件
- 路由的中间件
- 错误处理中间件
- 方法重写的中间件
- 输出缓冲中间件
- 内容长度中间件
- 扩展功能
- 以 / 结尾的路由模式
- 获取当前路由
- 设置CORS
- 使用POST表单上传文件
- 第三方组件
- slim-session
- auth
- slim-api-skeleton
- dir