今天碰到过这样一个情况,我需要限制用户请求某个API接口的频率,比如登录、反馈等提交操作,经过一番搜索+折腾,总算是实现了。
在Laravel 5.2的新特性中增加了一个throttle中间件,通过它可以在路由层限制API访问的频率。例如限制频率为1分钟50次,如果一分钟内超过了这个限制,它就会响应:429: Too Many Attempts。
但我在项目中使用的是Lumen框架(它只有Laravel中的一部分功能),它并没有集成这个中间件,所以本文主要是讲述如何在Lumen框架中加入throttle中间件。
开始
首先我们要在app\Http\Middleware中新建ThrottleRequests.php文件。
并且把以下链接中的代码拷贝到这个文件中:
github.com/illuminate/…
接着修改文件中的命名空间:
namespace App\Http\Middleware;
复制代码
标记同一用户端请求
因为Lumen框架缺失部分功能,我们需要修改ThrottleRequests.php中的resolveRequestSignature方法:
protected function resolveRequestSignature($request){
return sha1(
$request->method() .
'|' . $request->server('SERVER_NAME') .
'|' . $request->path() .
'|' . $request->ip()
);
}
复制代码
抛出响应
throttle超过限制时抛出的是Illuminate\Http\Exceptions\ThrottleRequestsException,同样Lumen框架缺少这个文件,需要自己定义一下,在app/Exceptions中新建ThrottleException.php,写入以下代码:
<?php
namespace App\Exceptions;
use Exception;
class ThrottleException extends Exception{
protected $isReport = false;
public function isReport(){
return $this->isReport;
}
}
复制代码
在app/Exceptions/Handler.php捕获该抛出异常,在render方法增加以下判断:
if ($exception instanceof ThrottleException) {
return response([
'code' => $exception->getCode(),
'msg' => $exception->getMessage()
], 429);
}
复制代码
修改ThrottleRequests.php文件中的buildException方法:
protected function buildException($key, $maxAttempts){
$retryAfter = $this->getTimeUntilNextRetry($key);
$headers = $this->getHeaders(
$maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
$retryAfter
);
// 修改了这一行
return new ThrottleException('Too Many Attempts.', 429);
}
复制代码
需在文件头部中添加这一行:use App\Exceptions\ThrottleException;
注册中间件
在bootstrap/app.php中注册:
$app->routeMiddleware([
'throttle' => App\Http\Middleware\ThrottleRequests::class,
]);
复制代码
到这里我们就加入成功了,接着在路由中添加中间件即可:
$router->group(['middleware' => ['throttle:10,2']],function() use ($router){
$router->post('feedback','UserController@addFeedback');
});
复制代码
其中throttle:10,2表示的是2分钟内访问10次。
有话要说