本书未发布

中间件

未匹配的标注

如果我们查看传入的 HTTP 请求,每个请求都有 Laravel 的 index.php 文件处理并通过一系列处理后返回给浏览器。其中就包括一系列的中间件,每个中间件都会在请求到达应用程序核心之前对其进行处理。还有一些中间件是在核心响应后,且该响应还未返回浏览器时进行修改。

「译者注」怎么理解这句话呢?根据拦截时间节点,中间件就分为两种。

  • 请求到达 Controller 之前:例如权限验证、CSRF 验证中间件等。
  • 响应返回浏览器之前:例如 设置 Cookie 数据(AddQueuedCookiesToResponse)。

这就是为什么中间件适合做权限验证、验证 Token 或其他必要检查的原因,Laravel 还使用中间件把请求的空字符串转换为 null、加密 cookie 等。

创建中间件

中间件基本分为两种类型:

  1. 应用处理请求 之前 进行操作。
  2. 应用请求 之后 进行操作。

「译者注」你可以在 Laravel 文档的 中间件 进行查看。
在讨论两种类的中间件之前,首先在 src/Http 目录创建一个 Middleware 文件夹,用于存放所有的中间件。

前置中间件

前置中间件将在应用处理请求 之前 执行一些任务。通常,前置中间件采用以下形式:

<?php

namespace App\Http\Middleware;

use Closure;

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

        return $next($request);
    }
}

前置中间件的说明,让我们添加一个中间件,这个中间件的作用是如果请求中有 title 参数时,我们将对其首字母进行大写转换。(仅仅是个示例,如有雷同,纯属巧合)

创建一个名为 CapitalizeTitle.php 的文件,该文件提供了一个 handle() 方法,该方法同时接收当前请求和 $next 操作:

// 'src/Http/Middleware/CapitalizeTitle.php'
<?php

namespace JohnDoe\BlogPackage\Http\Middleware;

use Closure;

class CapitalizeTitle
{
    public function handle($request, Closure $next)
    {
        if ($request->has('title')) {
            $request->merge([
                'title' => ucfirst($request->title)
            ]);
        }

        return $next($request);
    }
}

测试前置中间件

虽然我们尚未在服务提供者中注册中间件,并且目前也没有在扩展包中使用它,但是我们依然要保证 handle() 方法正确执行。

tests/Unit 文件夹创建一个新的 CapitalizeTitleMiddlewareTest.php 单元测试文件。测试文件主要确认在中间件运行handle() 方法后,Request() 中的 title 参数值的首字母是否变大写了:

// 'tests/Unit/CapitalizeMiddlewareTest.php'
<?php

namespace JohnDoe\BlogPackage\Tests\Unit;

use Illuminate\Http\Request;
use JohnDoe\BlogPackage\Http\Middleware\CapitalizeTitle;
use JohnDoe\BlogPackage\Tests\TestCase;

class CapitalizeTitleMiddlewareTest extends TestCase
{
    /** @test */
    function it_capitalizes_the_request_title()
    {
        // 创建一个请求
        $request = new Request();

        // 设置 title 参数,值为全小写
        $request->merge(['title' => 'some title']);

        // 将请你求传递给中间件后,
        // 应该将标题的首字母变大写
        (new CapitalizeTitle())->handle($request, function ($request) {
            $this->assertEquals('Some title', $request->title);
        });
    }
}

后置中间件

后置中间件是对所有数据处理完成后,在返回响应前对其进行修改。通常,他采用下面的形式:

<?php

namespace App\Http\Middleware;

use Closure;

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

        // Perform action

        return $response;
    }
}

测试后置中间件

与前置中间件类似,我们可以在请求的 Response 上执行的中间件进行测试,并在其传递到下一层之前对其进行修改。例如,我们有一个 InjectHelloWorld 中间件,它的作用是在每次响应中注入 'Hello World' 字符串,通过下面的测试代码进行测试:

// 'tests/Unit/InjectHelloWorldMiddlewareTest.php'
<?php

namespace JohnDoe\BlogPackage\Tests\Unit;

use Illuminate\Http\Request;
use JohnDoe\BlogPackage\Http\Middleware\InjectHelloWorld;
use JohnDoe\BlogPackage\Tests\TestCase;

class InjectHelloWorldMiddlewareTest extends TestCase
{
    /** @test */
    function it_checks_for_a_hello_word_in_response()
    {
        // 创建一个请求
        $request = new Request();

        // 将请求传入中间件,它会把 'Hello World' 插入到响应中
        $response = (new InjectHelloWorld())->handle($request, function ($request) { });

        $this->assertStringContainsString('Hello World', $response);
    }
}

现在我们看到 handle() 方法可以正确执行,下面我们看一下注册中间件的两个选项:全局 vs 指定路由

全局中间件

顾名思义,全局中间件的作用域是全局应用的,每个请求都将经过这个中间件处理。

如果我们希望检查标题在全局应用,则需要将 CapitalizeTitle 中间件添加到 Http\Kernel 文件中你那个,千万不要加到  Console Kernel 中:

// 'BlogPackageServiceProvider.php'
use Illuminate\Contracts\Http\Kernel;
use JohnDoe\BlogPackage\Http\Middleware\CapitalizeTitle;

public function boot()
{
  // other things ... 

  $kernel = $this->app->make(Kernel::class);
  $kernel->pushMiddleware(CapitalizeTitle::class);
}

这样会把 CapitalizeTitle 中间件加到应用程序的全局注册中间件数组中。

路由中间件

看到这里,你可能会说,在项目中不可能每个请求中都有 title 参数啊,设置为全局中间件会有问题。是的,这个中间件应该只针对创建、更新帖子的相关请求。

但是,目前我们示例的中间件是将所有具有 title 参数的请求都修改了,这不是我们想要的结果,解决方案就是使用路由中间件来解决。

我们需要在服务提供者的 boot() 方法中,先将 Router 类解析出来,在通过 aliasMiddleware 方法注册中间件,并注册别名为  capitalize ,后续在控制器中使用。

// 'BlogPackageServiceProvider.php'
use Illuminate\Routing\Router;
use JohnDoe\BlogPackage\Http\Middleware\CapitalizeTitle;

public function boot()
{
  // other things ...

  $router = $this->app->make(Router::class);
  $router->aliasMiddleware('capitalize', CapitalizeTitle::class);
}

在控制器的构造方法中应用 capitalize 中间件:

// 'src/Http/Controllers/PostController.php'
class PostController extends Controller
{
    public function __construct()
    {
        $this->middleware('capitalize');
    }

    // other methods... (will use this middleware)
}

测试

无论是全局中间件还是路由中间件,我们都可以请求时测试中间件是否适用。

添加新测试到 CreatePostTest 文件中,在该测试中,我们假设帖子保存时,需要将标题的首字母大写。

// 'tests/Feature/CreatePostTest.php'
/** @test */
function creating_a_post_will_capitalize_the_title()
{
    $author = factory(User::class)->create();

    $this->actingAs($author)->post(route('posts.store'), [
        'title' => 'some title that was not capitalized',
        'body' => 'A valid body',
    ]);

    $post = Post::first();

    // 'New: ' was added by our event listener
    $this->assertEquals('New: Some title that was not capitalized', $post->title);
}

随着测试变为绿色通过状态,我们完成了把中间件添加到扩展包的整个过程。

本文章首发在 LearnKu.com 网站上。

本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://learnku.com/docs/laravel-package...

译文地址:https://learnku.com/docs/laravel-package...

上一篇 下一篇
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
贡献者:1
讨论数量: 0
发起讨论 只看当前版本


暂无话题~