中间件
你可以在你的 Slim 应用的请求对象和响应对象__之前__和__之后__执行代码,这就是__中间件__。为什么需要做这些?假如你需要在你的应用中防止跨域攻击(cross-site request forgery,CSRF)。你可能就需要在你的应用启动之前添加验证,这时候就可以使用中间件。
什么是中间件
从技术角度上讲,中间件就是一个需要接受三个参数的 回调对象( callable )。三个参数分别是:
- \Psr\Http\Message\ServerRequestInterface - PSR7请求对象
- \Psr\Http\Message\ResponseInterface - PSR7响应对象
- callable - 下一层中间件的回调对象
中间件是如何工作的
不同的框架的中间件的使用方式是不同的。在Slim中中间件以同心圆的形式包裹着你的核心应用。每一个新增的中间件会包裹着之前已经添加到中间件,所以同心圆结构会不断向外扩张。
在运行Slim的应用的时候,请求对象和响应对象会从最外层的中间件开始进入,不断向内深入,直到穿过所有 中间件结构抵达核心应用。在核心应用分派完路由之后,返回的响应对象会从最内层的中间件开始,由内向外穿过所有的中间件。穿过最外层的中间件之后,最终响应对象会被序列化成最原始的HTTP消息,返回给客户端。下面的图表很清晰的说明了中间件的执行流程:
怎么创建中间件
中间件是一个可以接受三个参数的回调对象:请求对象,响应对象,下一个中间件。每一个中间件__必须__返回一个实现了 \Psr\Http\Message\ResponseInterface
接口的实例。
闭包实现的中间件示例
<?php
/**
* Example middleware closure
*
* @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* @param \Psr\Http\Message\ResponseInterface $response PSR7 response
* @param callable $next Next middleware
*
* @return \Psr\Http\Message\ResponseInterface
*/
function ($request, $response, $next) {
$response->getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
};
通过带 __invoke
方法的类实现的中间件
下面的示例是通过魔术方法 __invoke
实现的:
<?php
class ExampleMiddleware
{
/**
* Example middleware invokable class
*
* @param \Psr\Http\Message\ServerRequestInterface $request PSR7 request
* @param \Psr\Http\Message\ResponseInterface $response PSR7 response
* @param callable $next Next middleware
*
* @return \Psr\Http\Message\ResponseInterface
*/
public function __invoke($request, $response, $next)
{
$response->getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
}
}
你可以通过下面的方式使用上面的中间件类,使用 ->add( new ExampleMiddleware() );
$subject 指的是$app, Route, Group
$subject->add( new ExampleMiddleware() );
如何添加中间件
你可以给你的Slim应用,或者单独的路由或者路由群组添加中间件,因为Slim应用和路由实现了同样的中间件接口,可以接受同样的中间件。
应用中间件
应用中间件在所有进入应用的HTTP的请求都会被调用。通过调用 add()
方法来实现给Slim应用添加应用中间件。下面是给Slim应用添加闭包中间件的示例:
<?php
$app = new \Slim\App();
$app->add(function ($request, $response, $next) {
$response->getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
});
$app->get('/', function ($req, $res, $args) {
echo ' Hello ';
});
$app->run();
输出结果:
BEFORE Hello AFTER
路由中间件
路由中间件只会在匹配当前HTTP请求或者URI的路由中才会被调用。我们在调用了Slim应用的路由(e.g., get() or post())之后,立即指定路由中间件。每一条路由都会返回一个实现 \Slim\Route
的实例。\Slim\Route
也实现了与Slim应用一样的中间件接口。添加中间件使用 add()
。下面是添加路由中间件的示例:
<?php
$app = new \Slim\App();
$mw = function ($request, $response, $next) {
$response->getBody()->write('BEFORE');
$response = $next($request, $response);
$response->getBody()->write('AFTER');
return $response;
};
$app->get('/', function ($req, $res, $args) {
echo ' Hello ';
})->add($mw);
$app->run();
输出结果:
BEFORE Hello AFTER
路由群组中间件
除了整个应用和标准路由允许添加中间件外,我们还定义了 group() 方法同时给多个路由添加中间件。路由群组中间件__只有__在当前HTTP请求和URI能够匹配 路由群组里的某一条路由之后才会被调用。路由群组添加中间件是在 group() 之后使用 add() 方法,下面是使用给路由群组添加中间件的示例:
<?php
require_once __DIR__.'/vendor/autoload.php';
$app = new \Slim\App();
$app->get('/', function ($request, $response) {
return $response->getBody()->write('Hello World');
});
$app->group('/utils', function () use ($app) {
$app->get('/date', function ($request, $response) {
return $response->getBody()->write(date('Y-m-d H:i:s'));
});
$app->get('/time', function ($request, $response) {
return $response->getBody()->write(time());
});
})->add(function ($request, $response, $next) {
$response->getBody()->write('It is now ');
$response = $next($request, $response);
$response->getBody()->write('. Enjoy!');
return $response;
});
访问 /utils/date
输出下面的结果:
It is now 2015-07-06 03:11:01. Enjoy!
访问 /utils/time
输出下面的结果:
It is now 1436148762. Enjoy!
访问 /
网站根目录则不会有中间件的效果,输出如下:
Hello World
文档翻译的并不是很专业,仅供个人学习作用,建议看官方文档。
如有兴趣,请看下一篇 SLIM 3 文档(六)-依赖容器")