laravel 5.6 中 api 路由的 ThrottleRequestMiddleware
ThrottleRequestMiddleware for api route in laravel 5.6
我已经构建了 api 路由以将帖子作为 json 资源返回,当我将中间件 'apiThrottle:5,1' 放在 api 路由上时,它只显示结果两次然后在第三次它会生成 json 响应 'Too many attempts...'
但是当我将中间件添加到网络路由时,它会继续显示最大次数的结果(在本例中为 5 次),然后生成 'Too many attempts...'
网络路由:https://project.test/
Route::middleware('apiThrottle:5,1')->get('/', function (){
return 'Web Route';
});
Api 路线:https://project.test/api/
Route::middleware('apiThrottle:5,1')->get('/', function (){
return 'API Route';
});
中间件:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Middleware\ThrottleRequests;
class ThrottleRequestsMiddleware extends ThrottleRequests
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param int $maxAttempts
* @param int $decayMinutes
* @return mixed
*/
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
{
$key = $this->resolveRequestSignature($request);
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
return $this->buildJsonResponse($key, $maxAttempts);
}
$this->limiter->hit($key, $decayMinutes);
$response = $next($request);
return $this->addHeaders(
$response, $maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts)
);
}
/**
* Create a 'too many attempts' JSON response.
*
* @param string $key
* @param int $maxAttempts
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function buildJsonResponse($key, $maxAttempts)
{
$response = new JsonResponse([
'error' => [
'code' => 429,
'message' => 'Too many attempts, please slow down the request.',
],
], 429);
$retryAfter = $this->limiter->availableIn($key);
return $this->addHeaders(
$response, $maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
$retryAfter
);
}
}
Kernel.php
:
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
.
.
.
'apiThrottle' => \App\Http\Middleware\ThrottleRequestsMiddleware::class,
];
网络路由headers:请求和响应
Content-Security-Policy: default-src 'none' ; script-src resource:;
X-Firefox-Spdy: h2
cache-control: no-cache, private
content-type: application/json
date: Wed, 18 Apr 2018 11:57:50 GMT
retry-after: 53
server: nginx/1.13.6
set-cookie: XSRF-TOKEN=eyJpdiI6ImpRdGlIXC9KbnV3QTY2MkpKbEZUQ3d3PT0iLCJ2YWx1ZSI6IlU5YXFkZmpVXC9nMHpXSW5zK2RUZFlTVTA0N0YzaE9TeURpSHJlV0FUb1NkXC9rN3dvUUdVbzgwNHQ4MCtDU04rYjBNSHB6U2dsUzZ6cys2cGY5N1dUQVE9PSIsIm1hYyI6IjAzMTRkZTdkNmEwMzk5ODZhMTQ2ODhmYTJjOWE2ODIzODQ1YzQ0OThhY2I2NWUxOTk5MDRmNDc0ZjBjMmNjMDgifQ%3D%3D; expires=Wed, 18-Apr-2018 13:57:50 GMT; Max-Age=7200; path=/
larablog_session=eyJpdiI6IkZqaGZVUk10bFNpb2hUc2xPMXdBbVE9PSIsInZhbHVlIjoiY0hoXC9qMHFWdENIOEZZK3BaUklhMjZtQ2JoS2IwQlRTV053clUyQVd0RmpxTlRmMlVRc1Q4QW8zYUMwZSs0dmFuM3U2N3A2ZGJCZTBaN2JwdHJDSE13PT0iLCJtYWMiOiI1YzQ0ZTNjOGI4NDI3ZDg4NTI5MDQyMDRiMTc3ODk3OGZkYjk4OWJmNzhkODFkM2UwZGU1MTM0NDdmNjIxYTY1In0%3D; expires=Wed, 18-Apr-2018 13:57:50 GMT; Max-Age=7200; path=/; HttpOnly
x-ratelimit-limit: 5
x-ratelimit-remaining: 0
x-ratelimit-reset: 1524052723
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Cache-Control: max-age=0
Connection: keep-alive
Cookie: XSRF-TOKEN=eyJpdiI6IkNibkJQOUM3clJscElSWGZrcDVoZGc9PSIsInZhbHVlIjoiMzJuZGljNnRzUFoxNVVOYVZscFJBRElqMHJGSGxYa2dqVXkwR0VtZlhpQUFLUkI4cHJnOFdLSGIwdEdmRnA2R2Y5WHRXZHNtTFwvUVVpc2xKM1lQVHV3PT0iLCJtYWMiOiJmNmRjMjQwMDllOWFhZGY2Y2Q2YTVmYzFjZGQxNTE4MTk3ZjRkYWUyMTA2OWY0Y2E3NGNjYTg5MmEzYTZmZjJkIn0%3D; larablog_session=eyJpdiI6IlwvUm55cHBaTVdnR21rbkJVU05DUXVBPT0iLCJ2YWx1ZSI6IkJlYVV3dXB6SUNjMkRrUHJ2bTN2R3RUa294aVpjelJlTVFCUENXVldpSzlKNWdYUTdqeHJtY1JEWXV5UUpvbUc0bHB4UG5hcGltTG9OSVYrdjBMS29BPT0iLCJtYWMiOiI0M2ZjYWMyNjY0MjFkYTlhZjZkZmQ5MjVkMDFhODZiODFiNjQ4OGIzOTViZTNmY2E2NmNhMjQzYmRiY2I4ODdhIn0%3D
Host: project.test
Referer: https://project.test/
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
API路由headers:请求和响应
Content-Security-Policy: default-src 'none' ; script-src resource:;
X-Firefox-Spdy: h2
cache-control: no-cache, private
content-type: application/json
date: Wed, 18 Apr 2018 11:55:55 GMT
retry-after: 57
server: nginx/1.13.6
x-ratelimit-limit: 60
x-ratelimit-remaining: 55
x-ratelimit-reset: 1524052612
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Cache-Control: max-age=0
Connection: keep-alive
Cookie: XSRF-TOKEN=eyJpdiI6InFHZVFHNDg3M2hpcG9rUHd2WnlQclE9PSIsInZhbHVlIjoiQW1oZFZXbFc3NFhwVnpBTGZsM0tKbGlBaDhUNkM0TXpqMzZTb1FHUUI4bFk5XC9wWEdyK0lOZThyZ25vT2FLUHN2MXFZZGtuaVFta1lLdzBxZU9FdnR3PT0iLCJtYWMiOiJhMjM3MzA5ZDI1MmNjMjE0YTkyZjA4ODAyMzNmNDQwM2JjNTJjYzY4NzcyYWIwYWE5NjdmMWI0OTgwNjg5OGY4In0%3D; larablog_session=eyJpdiI6IjhTMnZTSmhMd3lSOUlLZzREd1BEN0E9PSIsInZhbHVlIjoiMVh2SFh4azM2RTJxRWFkV0g4YUxMUmRhVyt0bUVOYkZzbnZLRnBVb1gxQXB5OUQ2Y2lFZmNpMGt0NmNXSVlKSDFoWlE1aVZra2ROZEtYSzNpRzlVbGc9PSIsIm1hYyI6IjhmMDNmNzgzYWViODU3NzQ0MTExMzBlNWI1OTc0MTY5YzI2ZDZkZDQ2OThmZTM3NDJjZjA4MTkxN2Y0YTVhNmQifQ%3D%3D
Host: project.test
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
请指教,
api 节流阀宣布两次,因此命中计数两次。
对于所有 api 路由,在 app/Http/Kernel 上声明了节流中间件。php
'api' => [
'throttle:60,1',
'bindings',
],
要解决这个问题,你必须在内核文件中删除 api throttle,并且你可以在 api 路由文件中定义单独的 throttle 中间件组 app/routes/api.php如下:
// limit to 60 hits in 1 min
Route::group(['middleware' => ['throttle:60,1'], 'prefix' => 'foo'], function () {
Route::get('/', 'FooController@fooMethod');
});
// limit to 500 hits in 1 min
Route::group(['middleware' => ['throttle:500,1'], 'prefix' => 'bar'], function () {
Route::get('/', 'BarController@barMethod');
});
我试过了,效果很好。
php artisan cache:clear
我已经构建了 api 路由以将帖子作为 json 资源返回,当我将中间件 'apiThrottle:5,1' 放在 api 路由上时,它只显示结果两次然后在第三次它会生成 json 响应 'Too many attempts...'
但是当我将中间件添加到网络路由时,它会继续显示最大次数的结果(在本例中为 5 次),然后生成 'Too many attempts...'
网络路由:https://project.test/
Route::middleware('apiThrottle:5,1')->get('/', function (){
return 'Web Route';
});
Api 路线:https://project.test/api/
Route::middleware('apiThrottle:5,1')->get('/', function (){
return 'API Route';
});
中间件:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Middleware\ThrottleRequests;
class ThrottleRequestsMiddleware extends ThrottleRequests
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param int $maxAttempts
* @param int $decayMinutes
* @return mixed
*/
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1)
{
$key = $this->resolveRequestSignature($request);
$maxAttempts = $this->resolveMaxAttempts($request, $maxAttempts);
if ($this->limiter->tooManyAttempts($key, $maxAttempts)) {
return $this->buildJsonResponse($key, $maxAttempts);
}
$this->limiter->hit($key, $decayMinutes);
$response = $next($request);
return $this->addHeaders(
$response, $maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts)
);
}
/**
* Create a 'too many attempts' JSON response.
*
* @param string $key
* @param int $maxAttempts
* @return \Symfony\Component\HttpFoundation\Response
*/
protected function buildJsonResponse($key, $maxAttempts)
{
$response = new JsonResponse([
'error' => [
'code' => 429,
'message' => 'Too many attempts, please slow down the request.',
],
], 429);
$retryAfter = $this->limiter->availableIn($key);
return $this->addHeaders(
$response, $maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
$retryAfter
);
}
}
Kernel.php
:
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
.
.
.
'apiThrottle' => \App\Http\Middleware\ThrottleRequestsMiddleware::class,
];
网络路由headers:请求和响应
Content-Security-Policy: default-src 'none' ; script-src resource:;
X-Firefox-Spdy: h2
cache-control: no-cache, private
content-type: application/json
date: Wed, 18 Apr 2018 11:57:50 GMT
retry-after: 53
server: nginx/1.13.6
set-cookie: XSRF-TOKEN=eyJpdiI6ImpRdGlIXC9KbnV3QTY2MkpKbEZUQ3d3PT0iLCJ2YWx1ZSI6IlU5YXFkZmpVXC9nMHpXSW5zK2RUZFlTVTA0N0YzaE9TeURpSHJlV0FUb1NkXC9rN3dvUUdVbzgwNHQ4MCtDU04rYjBNSHB6U2dsUzZ6cys2cGY5N1dUQVE9PSIsIm1hYyI6IjAzMTRkZTdkNmEwMzk5ODZhMTQ2ODhmYTJjOWE2ODIzODQ1YzQ0OThhY2I2NWUxOTk5MDRmNDc0ZjBjMmNjMDgifQ%3D%3D; expires=Wed, 18-Apr-2018 13:57:50 GMT; Max-Age=7200; path=/
larablog_session=eyJpdiI6IkZqaGZVUk10bFNpb2hUc2xPMXdBbVE9PSIsInZhbHVlIjoiY0hoXC9qMHFWdENIOEZZK3BaUklhMjZtQ2JoS2IwQlRTV053clUyQVd0RmpxTlRmMlVRc1Q4QW8zYUMwZSs0dmFuM3U2N3A2ZGJCZTBaN2JwdHJDSE13PT0iLCJtYWMiOiI1YzQ0ZTNjOGI4NDI3ZDg4NTI5MDQyMDRiMTc3ODk3OGZkYjk4OWJmNzhkODFkM2UwZGU1MTM0NDdmNjIxYTY1In0%3D; expires=Wed, 18-Apr-2018 13:57:50 GMT; Max-Age=7200; path=/; HttpOnly
x-ratelimit-limit: 5
x-ratelimit-remaining: 0
x-ratelimit-reset: 1524052723
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Cache-Control: max-age=0
Connection: keep-alive
Cookie: XSRF-TOKEN=eyJpdiI6IkNibkJQOUM3clJscElSWGZrcDVoZGc9PSIsInZhbHVlIjoiMzJuZGljNnRzUFoxNVVOYVZscFJBRElqMHJGSGxYa2dqVXkwR0VtZlhpQUFLUkI4cHJnOFdLSGIwdEdmRnA2R2Y5WHRXZHNtTFwvUVVpc2xKM1lQVHV3PT0iLCJtYWMiOiJmNmRjMjQwMDllOWFhZGY2Y2Q2YTVmYzFjZGQxNTE4MTk3ZjRkYWUyMTA2OWY0Y2E3NGNjYTg5MmEzYTZmZjJkIn0%3D; larablog_session=eyJpdiI6IlwvUm55cHBaTVdnR21rbkJVU05DUXVBPT0iLCJ2YWx1ZSI6IkJlYVV3dXB6SUNjMkRrUHJ2bTN2R3RUa294aVpjelJlTVFCUENXVldpSzlKNWdYUTdqeHJtY1JEWXV5UUpvbUc0bHB4UG5hcGltTG9OSVYrdjBMS29BPT0iLCJtYWMiOiI0M2ZjYWMyNjY0MjFkYTlhZjZkZmQ5MjVkMDFhODZiODFiNjQ4OGIzOTViZTNmY2E2NmNhMjQzYmRiY2I4ODdhIn0%3D
Host: project.test
Referer: https://project.test/
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
API路由headers:请求和响应
Content-Security-Policy: default-src 'none' ; script-src resource:;
X-Firefox-Spdy: h2
cache-control: no-cache, private
content-type: application/json
date: Wed, 18 Apr 2018 11:55:55 GMT
retry-after: 57
server: nginx/1.13.6
x-ratelimit-limit: 60
x-ratelimit-remaining: 55
x-ratelimit-reset: 1524052612
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.5
Cache-Control: max-age=0
Connection: keep-alive
Cookie: XSRF-TOKEN=eyJpdiI6InFHZVFHNDg3M2hpcG9rUHd2WnlQclE9PSIsInZhbHVlIjoiQW1oZFZXbFc3NFhwVnpBTGZsM0tKbGlBaDhUNkM0TXpqMzZTb1FHUUI4bFk5XC9wWEdyK0lOZThyZ25vT2FLUHN2MXFZZGtuaVFta1lLdzBxZU9FdnR3PT0iLCJtYWMiOiJhMjM3MzA5ZDI1MmNjMjE0YTkyZjA4ODAyMzNmNDQwM2JjNTJjYzY4NzcyYWIwYWE5NjdmMWI0OTgwNjg5OGY4In0%3D; larablog_session=eyJpdiI6IjhTMnZTSmhMd3lSOUlLZzREd1BEN0E9PSIsInZhbHVlIjoiMVh2SFh4azM2RTJxRWFkV0g4YUxMUmRhVyt0bUVOYkZzbnZLRnBVb1gxQXB5OUQ2Y2lFZmNpMGt0NmNXSVlKSDFoWlE1aVZra2ROZEtYSzNpRzlVbGc9PSIsIm1hYyI6IjhmMDNmNzgzYWViODU3NzQ0MTExMzBlNWI1OTc0MTY5YzI2ZDZkZDQ2OThmZTM3NDJjZjA4MTkxN2Y0YTVhNmQifQ%3D%3D
Host: project.test
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
请指教,
api 节流阀宣布两次,因此命中计数两次。
对于所有 api 路由,在 app/Http/Kernel 上声明了节流中间件。php
'api' => [
'throttle:60,1',
'bindings',
],
要解决这个问题,你必须在内核文件中删除 api throttle,并且你可以在 api 路由文件中定义单独的 throttle 中间件组 app/routes/api.php如下:
// limit to 60 hits in 1 min
Route::group(['middleware' => ['throttle:60,1'], 'prefix' => 'foo'], function () {
Route::get('/', 'FooController@fooMethod');
});
// limit to 500 hits in 1 min
Route::group(['middleware' => ['throttle:500,1'], 'prefix' => 'bar'], function () {
Route::get('/', 'BarController@barMethod');
});
我试过了,效果很好。
php artisan cache:clear