Laravel:从自定义中间件油门组返回bool



我想在我的Laravel应用程序的中间件路由组中实现我自己的自定义逻辑。

我现在可以使用Laravel开箱即用的东西:

Route::middleware('throttle:10,1')->group(function () {
Route::get('/test/throttle', function() {
return response('OK', 200)->header('Content-Type', 'text/html');
});
});

因此,正如预期的那样,它返回默认的429代码视图作为HttpResponseException。


但我想覆盖节流中间件,只返回一个(bool(tooManyAttempts值作为函数参数:

Route::middleware('customthrottle:10,1')->group(function ($tooManyAttempts) {
Route::get('/test/throttle', function() {
if ($tooManyAttempts) {
return response("My custom 'Too many attempts' page only for this route", 200)->header('Content-Type', 'text/html');
} else {
return response('You are good to go yet, he-he', 200)->header('Content-Type', 'text/html');
}
});
});

我读过这篇文章,但我不知道如何以这种方式覆盖它,以便将一些值作为函数参数传递。它可以是(bool($tooManyAttempts值或至少是(int($maxAttempts值。


我应该覆盖什么方法以及以什么方式来实现这一点?(vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php(

<?php
namespace IlluminateRoutingMiddleware;
use Closure;
use IlluminateCacheRateLimiter;
use IlluminateCacheRateLimitingUnlimited;
use IlluminateHttpExceptionsHttpResponseException;
use IlluminateHttpExceptionsThrottleRequestsException;
use IlluminateSupportArr;
use IlluminateSupportInteractsWithTime;
use IlluminateSupportStr;
use RuntimeException;
use SymfonyComponentHttpFoundationResponse;
class ThrottleRequests
{
use InteractsWithTime;
/**
* The rate limiter instance.
*
* @var IlluminateCacheRateLimiter
*/
protected $limiter;
/**
* Create a new request throttler.
*
* @param  IlluminateCacheRateLimiter  $limiter
* @return void
*/
public function __construct(RateLimiter $limiter)
{
$this->limiter = $limiter;
}
/**
* Handle an incoming request.
*
* @param  IlluminateHttpRequest  $request
* @param  Closure  $next
* @param  int|string  $maxAttempts
* @param  float|int  $decayMinutes
* @param  string  $prefix
* @return SymfonyComponentHttpFoundationResponse
*
* @throws IlluminateHttpExceptionsThrottleRequestsException
*/
public function handle($request, Closure $next, $maxAttempts = 60, $decayMinutes = 1, $prefix = '')
{
if (is_string($maxAttempts)
&& func_num_args() === 3
&& ! is_null($limiter = $this->limiter->limiter($maxAttempts))) {
return $this->handleRequestUsingNamedLimiter($request, $next, $maxAttempts, $limiter);
}
return $this->handleRequest(
$request,
$next,
[
(object) [
'key' => $prefix.$this->resolveRequestSignature($request),
'maxAttempts' => $this->resolveMaxAttempts($request, $maxAttempts),
'decayMinutes' => $decayMinutes,
'responseCallback' => null,
],
]
);
}
/**
* Handle an incoming request.
*
* @param  IlluminateHttpRequest  $request
* @param  Closure  $next
* @param  string  $limiterName
* @param  Closure  $limiter
* @return SymfonyComponentHttpFoundationResponse
*
* @throws IlluminateHttpExceptionsThrottleRequestsException
*/
protected function handleRequestUsingNamedLimiter($request, Closure $next, $limiterName, Closure $limiter)
{
$limiterResponse = call_user_func($limiter, $request);
if ($limiterResponse instanceof Response) {
return $limiterResponse;
} elseif ($limiterResponse instanceof Unlimited) {
return $next($request);
}
return $this->handleRequest(
$request,
$next,
collect(Arr::wrap($limiterResponse))->map(function ($limit) use ($limiterName) {
return (object) [
'key' => md5($limiterName.$limit->key),
'maxAttempts' => $limit->maxAttempts,
'decayMinutes' => $limit->decayMinutes,
'responseCallback' => $limit->responseCallback,
];
})->all()
);
}
/**
* Handle an incoming request.
*
* @param  IlluminateHttpRequest  $request
* @param  Closure  $next
* @param  array  $limits
* @return SymfonyComponentHttpFoundationResponse
*
* @throws IlluminateHttpExceptionsThrottleRequestsException
*/
protected function handleRequest($request, Closure $next, array $limits)
{
foreach ($limits as $limit) {
if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
throw $this->buildException($request, $limit->key, $limit->maxAttempts, $limit->responseCallback);
}
$this->limiter->hit($limit->key, $limit->decayMinutes * 60);
}
$response = $next($request);
foreach ($limits as $limit) {
$response = $this->addHeaders(
$response,
$limit->maxAttempts,
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
);
}
return $response;
}
/**
* Resolve the number of attempts if the user is authenticated or not.
*
* @param  IlluminateHttpRequest  $request
* @param  int|string  $maxAttempts
* @return int
*/
protected function resolveMaxAttempts($request, $maxAttempts)
{
if (Str::contains($maxAttempts, '|')) {
$maxAttempts = explode('|', $maxAttempts, 2)[$request->user() ? 1 : 0];
}
if (! is_numeric($maxAttempts) && $request->user()) {
$maxAttempts = $request->user()->{$maxAttempts};
}
return (int) $maxAttempts;
}
/**
* Resolve request signature.
*
* @param  IlluminateHttpRequest  $request
* @return string
*
* @throws RuntimeException
*/
protected function resolveRequestSignature($request)
{
if ($user = $request->user()) {
return sha1($user->getAuthIdentifier());
} elseif ($route = $request->route()) {
return sha1($route->getDomain().'|'.$request->ip());
}
throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
}
/**
* Create a 'too many attempts' exception.
*
* @param  IlluminateHttpRequest  $request
* @param  string  $key
* @param  int  $maxAttempts
* @param  callable|null  $responseCallback
* @return IlluminateHttpExceptionsThrottleRequestsException
*/
protected function buildException($request, $key, $maxAttempts, $responseCallback = null)
{
$retryAfter = $this->getTimeUntilNextRetry($key);
$headers = $this->getHeaders(
$maxAttempts,
$this->calculateRemainingAttempts($key, $maxAttempts, $retryAfter),
$retryAfter
);
return is_callable($responseCallback)
? new HttpResponseException($responseCallback($request, $headers))
: new ThrottleRequestsException('Too Many Attempts.', null, $headers);
}
/**
* Get the number of seconds until the next retry.
*
* @param  string  $key
* @return int
*/
protected function getTimeUntilNextRetry($key)
{
return $this->limiter->availableIn($key);
}
/**
* Add the limit header information to the given response.
*
* @param  SymfonyComponentHttpFoundationResponse  $response
* @param  int  $maxAttempts
* @param  int  $remainingAttempts
* @param  int|null  $retryAfter
* @return SymfonyComponentHttpFoundationResponse
*/
protected function addHeaders(Response $response, $maxAttempts, $remainingAttempts, $retryAfter = null)
{
$response->headers->add(
$this->getHeaders($maxAttempts, $remainingAttempts, $retryAfter, $response)
);
return $response;
}
/**
* Get the limit headers information.
*
* @param  int  $maxAttempts
* @param  int  $remainingAttempts
* @param  int|null  $retryAfter
* @param  SymfonyComponentHttpFoundationResponse|null  $response
* @return array
*/
protected function getHeaders($maxAttempts,
$remainingAttempts,
$retryAfter = null,
?Response $response = null)
{
if ($response &&
! is_null($response->headers->get('X-RateLimit-Remaining')) &&
(int) $response->headers->get('X-RateLimit-Remaining') <= (int) $remainingAttempts) {
return [];
}
$headers = [
'X-RateLimit-Limit' => $maxAttempts,
'X-RateLimit-Remaining' => $remainingAttempts,
];
if (! is_null($retryAfter)) {
$headers['Retry-After'] = $retryAfter;
$headers['X-RateLimit-Reset'] = $this->availableAt($retryAfter);
}
return $headers;
}
/**
* Calculate the number of remaining attempts.
*
* @param  string  $key
* @param  int  $maxAttempts
* @param  int|null  $retryAfter
* @return int
*/
protected function calculateRemainingAttempts($key, $maxAttempts, $retryAfter = null)
{
return is_null($retryAfter) ? $this->limiter->retriesLeft($key, $maxAttempts) : 0;
}
}

正如注释中所建议的那样,有一种方法可以实现您在控制器逻辑中所做的事情,即重写Exceptions/Handler.php类中的异常。

public function render($request, Exception $exception)
{
if ($exception instanceof ThrottleRequestsException) {
return response('Too many attempts', 200)->header('Content-Type', 'text/html');
}
}

最新更新