Home  >  Q&A  >  body text

Rate limiting for successful requests only (Laravel 9)

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粉807239416P粉807239416262 days ago435

reply all(2)I'll reply

  • P粉986937457

    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.

    reply
    0
  • P粉512526720

    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>

    reply
    0
  • Cancelreply