Is there anyway to apply rate limiting to the route but for only success responses. Like for example if user sends request to send/code
endpoint 5 times and if all of them was successful then block the user to send request again. But if 2 of them was unsuccessful (like validation error or something) but 3 was successful then user should have 2 more attempts for the given time.
I know rate limiting checks before request get executed, then block or let the user to continue. But is there anyway to apply my logic or should I try to approach differently?
You would probably need to make your own middleware, but you can extend the ThrottleRequests
class and just customize how you want to handle responses:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Arr;
class ThrottleSuccess extends ThrottleRequests
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param array $limits
* @return \Symfony\Component\HttpFoundation\Response
*
* @throws \Illuminate\Http\Exceptions\ThrottleRequestsException
*/
protected function handleRequest($request, Closure $next, array $limits)
{
$response = $next($request); // call the controller first
if ($response->statusCode === 200) { // only hit limiter on successful response
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);
}
}
foreach ($limits as $limit) {
$response = $this->addHeaders(
$response,
$limit->maxAttempts,
$this->calculateRemainingAttempts($limit->key, $limit->maxAttempts)
);
}
return $response;
}
}
Then add your middleware to Kernel.php
:
protected $routeMiddleware = [
// ...
'throttle.success' => ThrottleSuccess::class,
// ...
];
Then use it in a route like the original throttle middleware:
Route::middleware('throttle.success:5,1')->group(function () {
// ...
});
Note: you may have to override handleRequestUsingNamedLimiter
if you want to return a custom response built from RateLimiter::for
, I have not done anything for that here.