[ Laravel 5.5 文档 ] 处理用户请求 —— HTTP 请求的过滤器:中间件

[ Laravel 5.5 文档 ] 处理用户请求 —— HTTP 请求的过滤器:中间件

http://laravelacademy.org/post/7812.html

简介

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

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

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

这篇教程将重点讲述中间件的定义、注册和使用。

定义中间件

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

php artisan make:middleware CheckToken

这个命令会在 app/Http/Middleware 目录下创建一个新的中间件类 CheckToken,在这个中间件中,我们只允许提供的 token 等于指定值 laravelacademy.org 的请求访问路由,否则,我们将跳转到 Laravel 学院网站:

?

正如你所看到的,如果 token != ‘laravelacademy.org‘,中间件会返回一个 HTTP 重定向到 Laravel 学院;否则,请求会被传递下去。将请求往下传递可以通过调用回调函数 $next 并传入当前 $request

注:此时只是定义好了中间件的逻辑,要让这个中间件生效,还要将其注册到指定路由中,我们很快就会在下面的注册中间件部分教你怎么做。

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

请求之前/之后的中间件

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

<?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;
    }
}

注册中间件

中间件分三类,分别是全局中间件、中间件组和指定路由中间件:

全局中间件

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

?

但除非真的需要,否则我们一般不会把业务级别的中间件放到全局中间件中。

分配中间件到指定路由

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

// 在 App\Http\Kernel 类中...

/**
 * 应用的路由中间件列表
 *
 * 这些中间件可以分配给路由组或者单个路由
 *
 * @var array
 */
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,
    ‘token‘ => CheckToken::class
];

中间件在 HTTP Kernel 中被定义后,可以使用 middleware 方法将其分配到路由:

Route::get(‘/‘, function () {
    //
})->middleware(‘token‘);

这样,当我们在浏览器中访问 http://blog.dev 时就会跳到 http://laravelacademy.org,只有当访问http://blog.dev?token=laravelacademy.org 时才能看到如下效果:

?

可以使用数组分配多个中间件到路由:

Route::get(‘/‘, function () {
    //
})->middleware(‘token‘, ‘auth‘);

分配中间件的时候还可以传递完整的类名(不过不推荐这种方式):

use App\Http\Middleware\CheckToken;

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

中间件组

有时候你可能想要通过指定一个键名的方式将相关中间件分到同一个组里面,这样可以更方便地将其分配到路由中,这可以通过使用 HTTP Kernel 提供的 $middlewareGroups 属性实现。

Laravel 自带了开箱即用的 web 和 api 两个中间件组,分别包含可以应用到 Web 和 API 路由的通用中间件:

/**
 * 应用的中间件组
 *
 * @var array
 */
protected $middlewareGroups = [
    ‘web‘ => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    ‘api‘ => [
        ‘throttle:60,1‘,
        ‘bindings‘,
    ],
];

中间件组使用和分配单个中间件同样的语法被分配给路由和控制器动作。再次申明,中间件组的目的只是让一次分配给路由多个中间件的实现更加方便:

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

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

默认情况下, RouteServiceProvider 自动将中间件组 web 应用到 routes/web.php 文件,将中间件组 api 应用到routes/api.php

?

当然我们可以自己设置自己的中间件组,以实现更灵活的中间件分配策略:

/**
 * 应用的中间件组.
 *
 * @var array
 */
protected $middlewareGroups = [
    ‘web‘ => [
        \App\Http\Middleware\EncryptCookies::class,
        \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
        \Illuminate\Session\Middleware\StartSession::class,
        // \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \App\Http\Middleware\VerifyCsrfToken::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
    ],

    ‘api‘ => [
        ‘throttle:60,1‘,
        ‘bindings‘,
    ],

    ‘blog‘ => [
        ‘token‘,
    ]
];

我们修改 routes/web.php 下面的中间件分配方式:

Route::group([‘middleware‘=>[‘blog‘]],function(){
    Route::get(‘/‘, function () {
        return view(‘welcome‘, [‘website‘ => ‘Laravel‘]);
    });

    Route::view(‘/view‘, ‘welcome‘, [‘website‘ => ‘Laravel学院‘]);
});

这样我们访问 http://blog.dev 和 http://blog.dev/view 的时候都要带上 token=laravelacademy.org 参数,否则就会跳转到 Laravel 学院网站。

中间件参数

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

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

<?php

namespace App\Http\Middleware;

use Closure;

class CheckRole
{
    /**
     * 处理输入请求
     *
     * @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}‘, function ($id) {
    //
})->middleware(‘role:editor‘);

根据上面的演示示例,这个功能实现起来也比较简单,就不再单独演示了。

终端中间件

终端中间件,可以理解为一个善后的后台处理中间件。有时候中间件可能需要在 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 方法将会接收请求和响应作为参数。定义了一个终端中间件之后,还需要将其加入到app/Http/Kernel.php 文件的全局中间件列表中。

当调用中间件上的 terminate 方法时,Laravel 将会从服务容器中取出一个该中间件的新实例,如果你想要在调用handle 和 terminate 方法时使用同一个中间件实例,则需要使用容器提供的 singleton 方法以单例的方式将该中间件注册到容器中。关于服务容器我们就会在后面讲到,暂时不深入展开了。

原文地址:https://www.cnblogs.com/wntd/p/9299281.html

时间: 2024-10-10 04:16:12

[ Laravel 5.5 文档 ] 处理用户请求 —— HTTP 请求的过滤器:中间件的相关文章

测试文档和用户说明书

最近把项目编写的差不多了,然后组长让我写测试文档和用户说明书,说明书给了一个以前他们写过的,然后就想着先把用户说明书写完,过程还好,但是在我写测试文档的时候发现自己真是一波三折. 说明文档的时候,大概有4个窗体,然后呢,我就是先把窗体界面截出来,然后就会在文档上写1-->2-->3-->4,第一步,单击某个按钮,弹出某个窗口,然后选择数据.... 很快,用户说明书写完了,感觉好简单,但是测试文档以前没有好好的写过,所以这次写的时候我还特意要了一个别人写过的文档,但是我对人家的项目也也没什

Laravel生成Word文档 - phpword

在项目实际开发或学习中,会遇到把数据导出生成word文档的需求.这里就用优雅.流行的laravel框架,来简单的教大家实现. phpword,它可以很方便的实现word文档的生成,同时可以在word中添加表格.目录.图片.超链接.页眉.页脚等功能强大. 安装phpWord 要求 强制性: PHP 5.3.3+ XML解析器扩展 Zend \ Escaper组件 Zend \ Stdlib组件 Zend \ Validator组件 可选的: -邮编扩展 - GD扩展 - XMLWriter扩展 -

Laravel 5.1 文档攻略 —— Eloquent:模型对象序列化

在写api的时候,数据一般是以json格式进行传输的,没有对象可以直接使用.这个时候,对数据的序列化转换就很重要,Eloquent提供了很方便的方法和约定,不仅可以转换,还可以控制里面的键值. 基本用法 将模型转化为数组 $user = App\User::with('roles')->first(); return $user->toArray(); 把一个模型转化成数组: $users = App\User::all(); return $users->toArray(); 把col

[ Laravel 5.1 文档 ] 架构 —— 应用目录结构

1.简介 Laravel应用默认的目录结构试图为不管是大型应用还是小型应用提供一个好的起点,当然,你可以自己按照喜好重新组织应用目录结构,Laravel对类在何处被加载没有任何限制——只要Composer可以自动载入它们即可. 2.根目录 新安装的Laravel应用包含许多文件夹: app目录包含了应用的核心代码: bootstrap目录包含了少许文件用于框架的启动和自动载入配置,还有一个cache文件夹用于包含框架生成的启动文件以提高性能: config目录包含了应用所有的配置文件: data

[ Laravel 5.1 文档 ] 服务 —— 任务调度

1.简介 在以前,开发者需要为每一个需要调度的任务编写一个Cron条目,这是很让人头疼的事.你的任务调度不在源码控制中,你必须使用SSH登录到服务器然后添加这些Cron条目.Laravel命令调度器允许你平滑而又富有表现力地在Laravel中定义命令调度,并且服务器上只需要一个Cron条目即可. 任务调度定义在app/Console/Kernel.php文件的schedule方法中,该方法中已经包含了一个示例.你可以自由地添加你需要的调度任务到Schedule对象. 1.1 开启调度 下面是你唯

[ Laravel 5.6 文档 ] 进阶系列 —— 任务调度

简介 Cron 是 UNIX.SOLARIS.LINUX 下的一个十分有用的工具,通过 Cron 脚本能使计划任务定期地在系统后台自动运行.这种计划任务在 UNIX.SOLARIS.LINUX下术语为 Cron Jobs.Crontab 则是用来记录在特定时间运行的 Cron 的一个脚本文件,Crontab 文件的每一行均遵守特定的格式: ? 我们可以在服务器上通过 crontab -e 来新增或编辑 Cron 条目,通过 crontab -l 查看已存在的 Cron 条目.更多关于 Cron

Laravel(PHP)使用Swagger生成API文档不完全指南 - 基本概念和环境搭建 - 简书

在PHPer中,很多人听说过Swagger,部分人知道Swagger是用来做API文档的,然而只有少数人真正知道怎么正确使用Swagger,因为PHP界和Swagger相关的资料实在是太少了.所以鄙人斗胆一试,希望能以本文帮助到大家了解Swagger,从此告别成天用Word.Markdown折腾API文档的日子. 什么是Swagger Swagger is a simple yet powerful representation of your RESTful API. With the lar

NoSQL数据模型详解(二)の文档模型

背景 "文档"是文档数据库中的主要概念.此类数据库可存放并获取文档.其格式可以是XML.JSON.BSON等.这些文档具备子属性.呈现树状数据结构,可以包含映射表.集合和纯量值.数据库中的文档彼此相似,但不必完全相同.文档数据库所存放的文档,就相当于键值数据库所存放的"值".文档数据库可以视为其值可查的键值数据库.流行的文档数据库有:MongoDB.CouchDB.Terrastore.OrientDB.RavenDB. 一致性 为了在Mongodb数据库中确保&q

使用 Swagger 文档化和定义 RESTful API

大部分 Web 应用程序都支持 RESTful API,但不同于 SOAP API——REST API 依赖于 HTTP 方法,缺少与 Web 服务描述语言(Web Services Description Language,WSDL)类似的语言来定义使用者与提供者之间的请求和响应结构.由于没有充分的合同服务,许多 REST API 提供者使用 Microsoft Word 文档或维基页面来记录 API 用法.这些格式使协作和文档版本控制变得很困难,尤其对于有许多 API 或资源的应用程序,或者