[ Laravel 5.1 文档 ] 基础 —— HTTP 中间件

1、简介

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

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

Laravel框架内置了一些中间件,包括维护模式中间件、认证、CSRF保护中间件等等。所有的中间件都位于app/Http/Middleware目录。

2、定义中间件

想要创建一个新的中间件,可以通过Artisan命令make:middleware

php artisan make:middleware OldMiddleware

这个命令会在app/Http/Middleware目录下创建一个新的中间件类OldMiddleware,在这个中间件中,我们只允许提供的age大于200的访问路由,否则,我们将用户重定向到主页:

<?php

namespace App\Http\Middleware;

use Closure;

class OldMiddleware
{
    /**
     * 返回请求过滤器
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->input('age') <= 200) {
            return redirect('home');
        }

        return $next($request);
    }

}

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

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

2.1 中间件之前/之后

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

<?php

namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware
{
    public function handle($request, Closure $next)
    {
        // 执行动作

        return $next($request);
    }
}

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

<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // 执行动作

        return $response;
    }
}

3、注册中间件

3.1 全局中间件

如果你想要中间件在每一个HTTP请求期间被执行,只需要将相应的中间件类放到app/Http/Kernel.php的数组属性$middleware中即可。

3.2 分配中间件到路由

如果你想要分配中间件到指定路由,首先应该在app/Http/Kernel.php文件中分配给该中间件一个简写的key,默认情况下,该类的$routeMiddleware属性包含了Laravel内置的入口中间件,添加你自己的中间件只需要将其追加到后面并为其分配一个key:

// 在 App\Http\Kernel 里中
protected $routeMiddleware = [
    'auth' => \App\Http\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,];

中间件在HTTP kernel中被定义后,可以在路由选项数组中使用$middleware键来指定中间件:

Route::get('admin/profile', ['middleware' => 'auth', function () {
    //
}]);

4、中间件参数

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

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

<?php

namespace App\Http\Middleware;

use Closure;

class RoleMiddleware
{
    /**
     * 运行请求过滤器
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string  $role
     * @return mixed
     * translator http://laravelacademy.org
     */
    public function handle($request, Closure $next, $role)
    {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }

        return $next($request);
    }

}

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

Route::put('post/{id}', ['middleware' => 'role:editor', function ($id) {
    //
}]);

5、可终止的中间件

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

<?php

namespace Illuminate\Session\Middleware;

use Closure;

class StartSession
{
    public function handle($request, Closure $next)
    {
        return $next($request);
    }

    public function terminate($request, $response)
    {
        // 存储session数据...
    }
}

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

当调用中间件上的terminate方法时,Laravel将会从服务容器中取出该中间件的新的实例,如果你想要在调用handleterminate方法时使用同一个中间件实例,则需要使用容器的singleton方法将该中间件注册到容器中。

扩展阅读:中间件实例教程 —— 中间件的创建使用及中间件参数定义

学院君 has written 980 articles

Laravel学院院长,终身学习者

积分:112777 等级:P12 职业:手艺人 城市:杭州

14 条回复

  1. 南京-前方 南京-前方 says:
    @学院君 我有个千古难题啊,你看看,在登录这个页面,我居然发现是这样的为什么?匪夷所思 不得其解而准确的 这地方就显示“”web“”
  2. 星空 星空 says:
    问下 中间件之后 是在哪个阶段运行的?请求处理之后?是控制器处理玩之后,准备response的时候吗?
  3. OakChen OakChen says:
    @ 郭佳 有没有相关的好的文章介绍来学习一下?
  4. OakChen OakChen says:
    问一下,关于“定义中间件”时的那段代码,1) 第三行的use Closure 中的closure是类?怎么来的? 2)第六行的handle方法中 Closure $next是什么意思? 3)第9行的 $next($request),这是什么用法?$next到底是函数还是变量?
  5. 学院君 学院君 says:
    @ 金黄色 管道模式借鉴了Linux中管道命令的说法 和责任链类似 后续设计模式系列中会讲到 这么说吧 可复用的请求过滤放到中间件 只是针对某个请求的放到Request
  6. smile smile says:
    @ 用户5253225361 非常感谢~学院菌的回答~ 在 《设计模式》 里面并没有看到 《管道模式》,所以想请问下《管道模式》是什么意思呢,是指解耦嘛? middleware确实比request更具用复用性,但是他俩在对于《请求过滤》这个环节上有木有什么明显的区别呢?换言之~就是什么时候该用middleware,什么时候用request呢?
  7. 学院君 学院君 says:
    @ RoughRoar 要从面向过程的思维转移到面向对象,中间件使用了管道模式 解耦了请求过滤和请求处理 不同的中间件可以实现插拔式组合 极大提高了代码灵活性和可复用性 这一点Request是做不到的 单独判断age大于200确实可以用请求实现 那如果我同时还要校验csrf、还要实现维护模式判断、用户权限判断呢 个人感觉Request实际上有点鸡肋

登录后才能进行评论,立即登录?