Is there a way to apply rate limiting to a route, but only on successful responses. For example, if a user sends requests to the send/code
endpoint 5 times, if all are successful, the user is blocked from sending requests again. However, if 2 of them fail (e.g. validation errors or other issues), but 3 succeed, the user should try 2 more times within the given time.
I know to do a rate limit check before executing the request and then block or let the user continue. But is there a way to apply my logic or should I try a different approach?
P粉9869374572024-01-06 17:02:10
This is the source code
use Illuminate\Support\Facades\RateLimiter; class CodeZiDotProTestRateLimit extends Controller{ public function test_rate_limit_only_success(Request $request){ // Step 1: Validate the request data $validator = Validator::make($request->all(), [ 'name' => 'required|string', 'email' => 'required|email', 'password' => 'required|min:8', ]); if ($validator->fails()) { return response()->json(['errors' => $validator->errors()], 422); } // Step 2: Apply rate limiting to this controller action $key = 'test_rate_limit_only_success_by_ip_'.request()->ip(); if (RateLimiter::tooManyAttempts($key,10)) { return response()->json(['errors' => 'You have made too much in a short time. Please wait after 1 minute'], 422); } else { RateLimiter::hit($key, 60); } }
}
Suppose my URL is Example.com/test_Rate_Limit_only_success.
In this example, when the user sends a request to the system, the application still validates the request (if an error occurs, the user will send an unlimited request). With the data valid, the speed limiting part will start working.
P粉5125267202024-01-06 09:10:49
You may need to make your own middleware, but you can extend the ThrottleRequests
class and customize how you want to handle the response:
<?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 routing like the original throttle middleware:
Route::middleware('throttle.success:5,1')->group(function () { // ... });
NOTE: If you want to return a custom response built from RateLimiter::for
you may have to override handleRequestUsingNamedLimiter
, I haven't done anything for that here. < /p>