You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel自定义throttle节流中间件返回布尔值参数的实现方案

自定义Laravel限流中间件实现方案

前置说明

你预期的直接给Route::group闭包传$tooManyAttempts参数的写法无法实现:路由组闭包在项目启动、路由注册阶段执行,而中间件逻辑在实际收到用户请求阶段才运行,二者运行时机完全不同,参数无法跨阶段传递。
正确方案是将限流状态写入请求实例的属性,在路由/控制器中直接从请求对象读取即可。

具体实现步骤

1. 新建自定义限流中间件

不要修改vendor目录下的原生源码,在app/Http/Middleware目录下新建CustomThrottle.php,继承原生ThrottleRequests类,重写handleRequest方法:

<?php

namespace App\Http\Middleware;

use Illuminate\Routing\Middleware\ThrottleRequests as BaseThrottleRequests;
use Closure;
use Illuminate\Http\Request;

class CustomThrottle extends BaseThrottleRequests
{
    /**
     * 重写限流处理逻辑,不抛异常,将限流状态写入请求属性
     */
    protected function handleRequest($request, Closure $next, array $limits)
    {
        $tooManyAttempts = false;
        $triggeredLimit = null;
        $retryAfter = 0;

        foreach ($limits as $limit) {
            if ($this->limiter->tooManyAttempts($limit->key, $limit->maxAttempts)) {
                $tooManyAttempts = true;
                $triggeredLimit = $limit;
                $retryAfter = $this->getTimeUntilNextRetry($limit->key);
                break;
            }
            // 保留原有的计数逻辑
            $this->limiter->hit($limit->key, $limit->decayMinutes * 60);
        }

        // 将限流相关参数写入请求属性
        $request->attributes->set('tooManyAttempts', $tooManyAttempts);
        if ($tooManyAttempts) {
            $request->attributes->set('maxAttempts', $triggeredLimit->maxAttempts);
            $request->attributes->set('retryAfter', $retryAfter);
            $request->attributes->set('remainingAttempts', 0);
        } else {
            $firstLimit = $limits[0];
            $request->attributes->set('maxAttempts', $firstLimit->maxAttempts);
            $request->attributes->set('remainingAttempts', $this->calculateRemainingAttempts($firstLimit->key, $firstLimit->maxAttempts));
        }

        $response = $next($request);

        // 保留原有限流响应头添加逻辑
        foreach ($limits as $limit) {
            $response = $this->addHeaders(
                $response,
                $limit->maxAttempts,
                $this->calculateRemainingAttempts($limit->key, $limit->maxAttempts, $tooManyAttempts ? $retryAfter : null)
            );
        }

        return $response;
    }
}

2. 注册中间件

打开app/Http/Kernel.php,在$routeMiddleware数组中新增注册项:

protected $routeMiddleware = [
    // 其他原有中间件...
    'customthrottle' => \App\Http\Middleware\CustomThrottle::class,
];

3. 使用示例

路由定义时直接从请求对象读取相关参数即可:

Route::middleware('customthrottle:10,1')->group(function () {
   Route::get('/test/throttle', function(\Illuminate\Http\Request $request) {
      // 读取限流状态
      if ($request->attributes->get('tooManyAttempts')) {
          $retryAfter = $request->attributes->get('retryAfter');
          return response("My custom 'Too many attempts' page only for this route,{$retryAfter}秒后重试", 200)
              ->header('Content-Type', 'text/html');
      } else {
          $remaining = $request->attributes->get('remainingAttempts');
          return response("You are good to go yet, he-he,剩余尝试次数:{$remaining}", 200)
              ->header('Content-Type', 'text/html');
      }
   });
});

内容的提问来源于stack exchange,提问作者mr.incredible

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 09:09:11