Laravel 文档阅读:中间件

翻译、衍生自:https://learnku.com/docs/laravel/5.5/middleware

简介

中间件用来过滤项目中的 HTTP 请求,实际上 Laravel 项目中大量使用了中间件。例如,Laravel 中有一个验证用户是否认证的中间件,如果用户没认证,就跳转到登录页面;如果认证了,就会允许进一步的操作。

当然,中间件的作用不只在认证上。CORS 中间件负责给项目中的响应设定正确的表头(Headers );日志中间件负责记录项目中处理的所有请求信息。

Laravel 框架中包含了几个中间件,包括负责处理认证和 CSRF 保护的。所有这些中间件位于 app/Http/Middleware 目录下。

定义中间件

使用 Artisan 命令 make:middleware 创建中间件:

php artisan make:middleware CheckAge

这个命令会在 app/Http/Middleware 目录下创建一个 CheckAge 类。在这个中间件里,我们设定年龄大于 200 岁的可以进一步操作(这不扯吗?),小于等于 200 岁的重定向到用户控制台地址 /home

<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->age <= 200) {
            return redirect('home');
        }

        return $next($request);
    }
}

允许请求的进一步操作,只需将请求实例 $request 放入回调 $next 中即可。

可以把中间件设想成,在最终到达想要执行的业务代码前的一层层「过滤网」,请求必须通过所有的中间件才能达到最终要执行的业务逻辑,否则被中间任何一个中间件拒绝,都会导致请求失败的。

中间件处理时机

一个中间件在请求之前或者之后进行任务处理,取决于中间件的代码逻辑放置的位置。例如,下面的中间件在处理请求 之前 进行了一些任务操作:

<?php

namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware
{
    public function handle($request, Closure $next)
    {
        // Perform action

        return $next($request);
    }
}

而下面的中间件是在处理请求 之后 才进行任务操作:

<?php

namespace App\Http\Middleware;

use Closure;

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

        // Perform action

        return $response;
    }
}

注册中间件

全局中间件

如果一个中间件在每次的 HTTP 请求时都要用到,那么把它列入到 app/Http/Kernel.php 文件里的 $middleware 数组中即可。列入在 $middleware 数组中的中间件又称为全局中间件。

为路由使用中间件

使用在路由身上的中间件是在 app/Http/Kernel.php$routeMiddleware 数组属性中定义的。如果你创建了一个新的给路由使用的中间件,就需要在它添加到 $routeMiddleware 这个数组里,并给这个中间件一个 key——相当于中间件的名字。

// Within App\Http\Kernel Class...

protected $routeMiddleware = [
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];

使用中间件时,就要用到了这个 key。

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

也可以在一个路由上,同时使用多个中间件。

Route::get('/', function () {
    //
})->middleware('first', 'second');

也可以使用中间件的包含命名空间的完整类名(又称完全限定类名)使用中间件:

use App\Http\Middleware\CheckAge;

Route::get('admin/profile', function () {
    //
})->middleware(CheckAge::class);

中间件组

中间件组中包含多个中间件,但它的使用和中间件是完全一样的。中间件组在 app/Http/Kernel.php$middlewareGroups 属性中定义,每个中间件组还有一个对应的 key。

Laravel 项目中,预设并使用了两个中间件组:webapi,它们分别用在了 routes/web.phproutes/api.php 上,前者是定义 Web 接口路由的地方,后者是定义 API 接口路由的地方。这两种类型的接口,各自都有一些通用的中间件,然后放在了一个组里进行管理。

/**
 * The application's route middleware groups.
 *
 * @var array
 */
protected $middlewareGroups = [
    'web' => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    'api' => [
        'throttle:60,1',
        'auth:api',
    ],
];

已经说过,中间件组的使用和中间件是完全一样的,不过带来一个好处——一次定义,即可使用多个中间件。中间件组可以分配给路由和控制器 action 使用。

Route::get('/', function () {
    //
})->middleware('web');

Route::group(['middleware' => ['web']], function () {
    //
});

注意,routes/web.php 文件里的中间件已经默认使用了 web 中间件组,这是在 RouteServiceProvider 中定义的,上面的是例子演示,在 routes/web.php 中定义路由时,是无需为路由额外分配 web 中间件组的。

中间件参数

可以为中间件传递参数。例如,如果系统有一个需求——需要在验证角色后才允许认证用户的进一步操作。以下面的 CheckRole 中间件为例,它接受一个角色名作为参数。

中间件参数在 handle 方法的 $next 参数之后定义。

<?php

namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    /**
     * Handle the incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @param  string  $role
     * @return mixed
     */
    public function handle($request, Closure $next, $role)
    {
        if (! $request->user()->hasRole($role)) {
            // Redirect...
        }

        return $next($request);
    }

}

使用中间件参数的方式是在中间件后面加上一个 :,然后在 : 后面跟上你要传递给中间件的参数。

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

在 HTTP 响应之后……

有时,中间件需要在 HTTP 响应发送到浏览器之后才做处理工作。例如,包含在 Laravel 中的会话中间件会在响应发送到浏览器后,再去保存会话数据。针对这样的需求,需要在中间件中定义一个 terminate 方法,它会在响应发送到浏览器之后自动被调用。

<?php

namespace Illuminate\Session\Middleware;

use Closure;

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

    public function terminate($request, $response)
    {
        // Store the session data...
    }
}

terminate 方法接受两个参数,请求实例和响应实例。定义好后,将这个中间件放在 app/Http/Kernel.php 添加在中间件列表中即可。

在调用 terminate 方法时,Laravel 是从服务容器中找到并创建一个新的中间件实例使用的,如果你只要使用同一个中间件实例,那么就要使用容器的 singleton 方法注册中间件了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
本帖由 Summer 于 6年前 加精
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 2

Route::get('/', function () { // })->middleware('web');

如果路由中间件数组也有web的key,那么上面代码的web是web组还是路由web中间件??

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!