HTTP 中间件


1、简介

HTTP中间件提供了一个便利的机制来过滤进入应用的HTTP请求。例如,Lumen包含了一个中间件来验证用户是否经过授权,如果用户没有经过授权,中间件会将用户重定向到登录页面,否则如果用户经过授权,中间件就会允许请求继续往前进入下一步操作。

当然,除了认证之外,中间件还可以被用来处理更多其它任务。比如:CORS中间件可以用于为离开站点的响应添加合适的头(跨域);日志中间件可以记录所有进入站点的请求。

2、定义中间件

中间件通常都放在app/Http/Middleware目录下。想要创建一个新的中间件,需要在新创建的中间件中重写handle方法。在下面中间件中,我们只允许提供的age大于200的访问路由,否则,我们将用户重定向到主页:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class OldMiddleware
  5. {
  6. /**
  7. * 返回请求过滤器
  8. *
  9. * @param \Illuminate\Http\Request $request
  10. * @param \Closure $next
  11. * @return mixed
  12. */
  13. public function handle($request, Closure $next)
  14. {
  15. if ($request->input('age') <= 200) {
  16. return redirect('home');
  17. }
  18. return $next($request);
  19. }
  20. }

正如你所看到的,如果age<=200,中间件会返回一个HTTP重定向到客户端;否则,请求会被传递下去。将请求往下传递可以通过调用回调函数$next

理解中间件的最好方式就是将中间件看做HTTP请求到达目标之前必须经过的“层”,每一层都会检查请求甚至会完全拒绝它。

2.1 中间件之前/之后

一个中间件是否请求前还是请求后执行取决于中间件本身。比如,以下中间件会在请求处理前执行一些任务:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class BeforeMiddleware
  5. {
  6. public function handle($request, Closure $next)
  7. {
  8. // 执行动作
  9. return $next($request);
  10. }
  11. }

然而,下面这个中间件则会在请求处理后执行其任务:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class AfterMiddleware
  5. {
  6. public function handle($request, Closure $next)
  7. {
  8. $response = $next($request);
  9. // 执行动作
  10. return $response;
  11. }
  12. }

3、注册中间件

3.1 全局中间件

如果你想要中间件在每一个HTTP请求期间被执行,只需要将相应中间件类放到bootstrap/app.php文件的$app->middleware()调用中即可。

3.2 分配中间件到路由

如果你想要分配中间件到指定路由,首先应该在bootstrap/app.php文件中分配给该中间件一个简写的key,默认情况下,$app->routeMiddleware()方法包含了Lumen自带的入口中间件,添加你自己的中间件只需要将其追加到后面并为其分配一个key:

  1. $app->routeMiddleware([
  2. 'old' => 'App\Http\Middleware\OldMiddleware',]);

中间件在入口文件中被定义好了之后,可以在路由选项数组中使用middleware键来指定中间件:

  1. $app->get('admin/profile', ['middleware' => 'auth', function () {
  2. //
  3. }]);

4、中间件参数

中间件还可以接收额外的自定义参数,比如,如果应用需要在执行动作之前验证认证用户是否拥有指定的角色,可以创建一个RoleMiddleware来接收角色名作为额外参数。

额外的中间件参数会在$next参数之后传入中间件:

  1. <?php
  2. namespace App\Http\Middleware;
  3. use Closure;
  4. class RoleMiddleware
  5. {
  6. /**
  7. * 运行请求过滤器
  8. *
  9. * @param \Illuminate\Http\Request $request
  10. * @param \Closure $next
  11. * @param string $role
  12. * @return mixed
  13. * translator http://laravelacademy.org
  14. */
  15. public function handle($request, Closure $next, $role)
  16. {
  17. if (! $request->user()->hasRole($role)) {
  18. // Redirect...
  19. }
  20. return $next($request);
  21. }
  22. }

中间件参数可以在定义路由时通过:分隔中间件名和参数名来指定,多个中间件参数可以通过逗号分隔:

  1. $app->put('post/{id}', ['middleware' => 'role:editor', function ($id) {
  2. //
  3. }]);

5、中止中间件

有时候中间件可能需要在HTTP响应发送到浏览器之后做一些工作。比如,Lumen自带的“session”中间件会在响应发送到浏览器之后将session数据写到存储器中,为了实现这个,定义一个“终结者”中间件并添加terminate方法到这个中间件:

  1. <?php
  2. namespace Illuminate\Session\Middleware;
  3. use Closure;
  4. class StartSession
  5. {
  6. public function handle($request, Closure $next)
  7. {
  8. return $next($request);
  9. }
  10. public function terminate($request, $response)
  11. {
  12. // 存储session数据...
  13. }
  14. }

terminate方法将会接收请求和响应作为参数。一旦你定义了一个终结中间件,应该将其加入到入口文件的全局中间件列表中。