[译]Laravel 5.0 之 Middleware (Filter-Style)
本文译自 Matt Stauffer 的系列文章.
如果你有阅读我之前的 Laravel 5.0 系列文章,你可能已经注意到路由过滤器(route filters)的变化:它们先是移到了单独的目录和类结构,然后就莫名其妙地消失了。你可能还留意到在原本应该是路由过滤器的地方,变成了对 Middleware 的引用。
实际上给 Laravel 应用添加自定义的 Middleware 在以前的版本中就有了。 Chris Fidao 的 HTTP Middleware in Laravel 4.1 对 middleware 做了全面的介绍,包括 middleware 在 Laravel 4.1 版本中的工作机制。
提示:过滤器在 Laravel 核心代码中依然存在,所以你依然可以使用。但是在需要对路由进行修饰时,更推荐采用的是 middleware.
Middleware 是什么?
Middleware 有点不好理解。你可以先看看下面这张从 StackPHP 借来的图。假设你的应用——路由,控制器,业务逻辑——是图中的绿色部分,从图中可以清晰地看到,用户请求先经由多个中间层才能到达你的应用,然后再经由更多的中间层进行处理。每个特定的中间层都可以在应用逻辑之前、之后进行处理,或者同时在应用逻辑之前和之后进行处理。
这就是 middleware 实现修饰模式的工作方式:它捕获请求,做一些处理,然后把处理后的请求对象返回给下一个堆栈层。
Laravel 默认使用 middleware 来处理加密/解密和 cookies 队列、读取和写入 sessions, 但除此之外你还可以用 middleware 来向请求/响应环中加入你需要的任何一种操作层。比如速率限制、自定义请求解析等。
怎么编写 middleware?
通过执行 artisan 命令:
$ php artisan make:middleware MyMiddleware
这条命令会生成一个简单的 middleware 文件,代码如下:
namespace AppHttpMiddleware;
use Closure;
use IlluminateContractsRoutingMiddleware;
class MyMiddleware implements Middleware {
/**
* 处理输入请求
*
* @param IlluminateHttpRequest $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
//
}
}
如你所见,所有 middleware 的基础是 handle
方法,它接受两个参数:
-
$request
: Illuminate Request 对象 -
$next
: Closure(匿名函数), 该函数把 request 对象传递给后续的 middleware.
还记得之前那个荒谬的“阻止奇数端口请求的 ValidatesWhenResolved 对象”的例子吗?很好,现在再把它拿过来,改成 middleware 风格的:
namespace AppHttpMiddleware;
use Closure;
use IlluminateContractsRoutingMiddleware;
class MyMiddleware implements Middleware {
/**
* Handle an incoming request.
*
* @param IlluminateHttpRequest $request
* @param Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
// Test for an even vs. odd remote port
if (($request->server->get('REMOTE_PORT') / 2) % 2 > 0)
{
throw new Exception("WE DON'T LIKE ODD REMOTE PORTS");
}
return $next($request);
}
}
如何使用 Middleware?
在 Laravel 5 中有两种主要的方法可以绑定 middleware. 两种方法都从 AppHttpKernel
开始。
你可能注意到了,新的 Kernel
类有两个属性: $middleware
和 $routeMiddleware
. 这两个属性都是 middleware 为元素的数组。在 $middleware
中的 middleware 会在每次请求时运行,而 $routeMiddleware
中的 middleware 必须被启用才会运行。
在本文写作时,$middleware
中默认包含的有五个 middlewares:
protected $middleware = [
'IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode',
'IlluminateCookieMiddlewareEncryptCookies',
'IlluminateCookieMiddlewareAddQueuedCookiesToResponse',
'IlluminateSessionMiddlewareStartSession',
'IlluminateViewMiddlewareShareErrorsFromSession',
'IlluminateFoundationHttpMiddlewareVerifyCsrfToken',
];
此外还有三个可选的 middlewares:
protected $routeMiddleware = [
'auth' = 'AppHttpMiddlewareAuthenticate',
'auth.basic' => 'IlluminateAuthMiddlewareAuthenticateWithBasicAuth',
'guest' => 'AppHttpMiddlewareRedirectIfAuthenticated',
];
从上面的代码中可以看到, 在新版本中默认可用的可选路由 middleware 与旧版本中默认可用的可选过滤器(filter)是一样的,除了一个例外——CSRF 表单保护在新版本中默认是对所有路由默认启用的——这非常重要。
在每次请求时执行 middleware
下面,我们从让自己的 middleware 在每次请求时都执行开始。很简单,只要把它加到 $middleware
数组中:
protected $middleware = [
'AppHttpMiddlewareMyMiddleware', // 这是自定义的
'IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode',
'IlluminateCookieMiddlewareEncryptCookies',
'IlluminateCookieMiddlewareAddQueuedCookiesToResponse',
'IlluminateSessionMiddlewareStartSession',
'IlluminateViewMiddlewareShareErrorsFromSession',
'IlluminateFoundationHttpMiddlewareVerifyCsrfToken',
];
现在每次请求时它都会被执行了。
在特定的路由上执行 middleware
OK, 接下来把我们的自定义 middleware 移到可选堆栈,要给它指定一个 key:
protected $routeMiddleware = [
'auth' = 'AppHttpMiddlewareAuthenticate',
'auth.basic' => 'IlluminateAuthMiddlewareAuthenticateWithBasicAuth',
'guest' => 'AppHttpMiddlewareRedirectIfAuthenticated',
'absurd' => 'AppHttpMiddlewareMyMiddleware', // 这是自定义的
];
现在我们可以在 routes.php 文件中或者在基础控制器(BaseController)中用 $this->middleware()
方法来调用自定义的 middleware 了。
在控制器中调用:
...
use IlluminateRoutingController;
class AwesomeController extends Controller {
public function __construct()
{
$this->middleware('csrf');
$this->middleware('auth', ['only' => 'update'])
}
}
在 routes.php 文件中调用:
// Routes.php
// Single route
$router->get("/awesome/sauce", "AwesomeController@sauce", ['middleware' => 'auth']);
// Route group
$router->group(['middleware' => 'auth'], function() {
// lots of routes that require auth middleware
});
如何通过 middleware 实现 before
和 after
过滤器?
我花了一些时间来研究这个问题,但 Taylor(译注:Laravel 框架作者) 指出了 "before" middleware 和 "after" middleware 的区别在于 middleware 的行为是发生在它调用 $next()
之前还是之后:
...
class BeforeMiddleware implements Middleware {
public function handle($request, Closure $next)
{
// Do Stuff
return $next($request);
}
}
...
class AfterMiddleware implements Middleware {
public function handle($request, Closure $next)
{
$response = $next($request);
// Do stuff
return $response;
}
}
如你所见, "before" middleware 先执行操作,然后把请求向堆栈传递。而 "after" middleware 是先调用 $next()
方法让请求被堆栈处理,之后再对它执行操作。
写在最后
如果你还不熟悉 middleware, 你的大脑可能会被它的概念纠缠一会儿。从我们考虑控制器及路由请求的常规思维方式的角度来说,过滤器(filter)会更容易理解一些。但 middleware ——这种在堆栈中传递单一请求,让它一点一点被处理的概念——其实更整洁、更简单、更灵活。
不仅如此,middleware 不只是在 Laravel 应用中处理请求的额外的一种强大而高效的手段,它在其它方面也能有很好的表现。Laravel 5.0 中的 middleware 语法与 StackPHP 的语法不完全兼容。但如果你采用基于 middleware 的架构来组织你的请求/响应堆栈,这是在依赖关系分离方向上的一个进步。而且要修改一个 Laravel middleware 使之可以在单独的 StackPHP 风格的语法下工作,也花不了多少工夫。
有任何意见,欢迎留下评论或者通过 Twitter 与 @stauffermatt 进行交流。
- 施工现场的机器人会不会逃跑?
- 不用@微信官方了,Python20行自动戴帽!
- BAT人工智能生态时局图:全面战争爆发前夜
- AI博弈论:DeepMind让智能体在非对称博弈中找纳什均衡
- 斯坦福吴恩达团队公布最大医学影像数据集
- Rokid祝明铭:大腿我们不抱,人机交互产品形态未定 | 变局者
- 腾讯AI让二子,柯洁还是输了
- AI创业者的“英雄联盟”,腾讯AI加速器二期项目招募开启
- PyTorch发布一周年:盘点社区和工程大事件,后来者居上态势已显?
- 2018年AI如何发展?普华永道做出了8点预测 | 报告下载
- 不正之风!机器学习论文里都有哪四大投机取巧的写作手法?
- 前端写一个月的原生 Android 是怎样一种体验?
- 给人挖矿还不自知 电脑已变黑客肉鸡
- 反序列化漏洞屡被黑客利用,危害巨大,代码怎样写才安全?
- php概述
- php教程
- php环境搭建
- PHP书写格式
- php变量
- php常量
- PHP注释
- php数组
- php字符串 string
- PHP整型 integer
- PHP浮点型 float
- php布尔型
- php数据类型之数组
- php数据类型之对象
- php数据类型之null
- php数据类型之间的转换
- php运算符
- php表达式
- PHP循环控制
- PHP流程控制
- php函数
- php全局变量
- PHP魔术变量
- php命名空间
- php 日期
- PHP包含文件
- php文件
- PHP 文件上传
- php Cookies
- php Sessions
- php email
- php安全email
- php错误处理
- PHP异常处理
- php过滤器
- PHP 高级过滤器
- php json
- php 表单
- PHP MySQL 简介
- PHP 连接 MySQL
- php创建数据库
- php 创建表
- php mysq 插入数据
- PHP MySQL 插入多条数据
- PHP MySQL 预处理语句
- php mysql 读取数据
- php mysql where
- PHP MySQL Order By
- PHP MySQL Update
- PHP MySQL Delete
- php ODBC
- VIO
- FPGA程序加载方式
- 国密SSL协议之性能测试
- 发现一个好看的手机壁纸网站,撸代码的手已经饥渴难耐了
- 还在为怎么学习Android苦恼?看完学会这些大牛资料,2年高级3年资深不是问题!
- 面试心得:一个BAT大厂面试者整理的Android面试题目,五轮面试后终于拿到Offer!
- 【Lighthouse教程】scrapy爬虫初探
- python 手把手教你基于搜索引擎实现文章查重
- Intellij IDEA 如何自动生成 serialVersionUID
- Spring事务是如何应用到你的业务场景中的?
- BFE.dev前端刷题#9. 解密消息 (Facebook面试题)
- JUnit 5 测试问题 must be static unless the test class is
- Intellij IDEA 如何自动生成 serialVersionUID
- 图解面试题:SQL存储过程有什么用?
- LeetCode-6.Z 字形变换 - 消费补偿算法