必须将laravel api限流响应改为json格式,具体操作包括:确认throttlerequests中间件已配置在api路由组中;在handler.php的render方法中捕获throttlerequestsexception并返回含code、message、retry_after字段的json响应;最后用curl或postman验证响应结构与头部是否正确。

当Laravel API因请求频率过高被限流时,客户端收到的是默认的429 HTML响应或空白页面,前端无法解析错误、用户得不到明确提示——必须让限流响应变成结构清晰、字段统一、含code/message的JSON格式。
确认限流中间件已启用且生效
打开 app/Http/Kernel.php,检查 $middlewareGroups['api'] 中是否包含 ThrottleRequests::class 或自定义限流中间件。若只在 web 组里配置了 throttle,API 请求根本不会触发限流逻辑。
运行 php artisan route:list --name=your-api-route 查看目标路由实际应用的中间件列表,确保输出中包含 throttle:max,minutes(例如 throttle:60,1)。
【关键前提】 限流中间件必须作用于 routes/api.php 中的路由;若误配在 web.php 且未加 ->middleware('api'),即使写了 throttle 也无效,请求直接绕过限流。
拦截并重写限流异常为JSON响应
打开 app/Exceptions/Handler.php,找到 render 方法,在 return parent::render($request, $exception); 前插入判断:
第一步:添加 use Illuminate\Http\Exceptions\ThrottleRequestsException; 在文件顶部。
第二步:在 render 方法内插入以下分支:
if ($exception instanceof ThrottleRequestsException && $request->expectsJson()) {
return response()->json([
'code' => 429,
'message' => 'Too many requests. Please try again later.',
'retry_after' => $exception->retryAfter(),
], 429);
}
注意:$exception->retryAfter() 返回的是秒数,前端可据此倒计时;若直接用 $exception->getHeaders()['Retry-After'] 可能为空,因为该头仅在响应发出后才设置,此处不可靠。
统一错误结构并兼容非JSON请求
方法一:保留HTML降级能力
不删除原有 HTML 错误页逻辑,只对 expectsJson() 的请求做 JSON 分支。这样浏览器直输 /api/xxx 仍能看到友好提示页,Postman 或 axios 调用则拿到 JSON。
方法二:强制所有限流响应走JSON(仅限纯API项目)
把 if 判断改成 if ($exception instanceof ThrottleRequestsException) { … },去掉 $request->expectsJson() 条件。这一步会令所有限流场景(包括浏览器访问)都返回 JSON,需确保前端团队知晓此约定。
【不可逆操作】 若选择方法二,请同步检查 nginx/Apache 配置,避免反向代理将 application/json 响应体错误地压缩或缓存,导致前端 fetch 解析失败。
验证限流响应是否符合预期
使用 curl 发起高频请求:
curl -H "Accept: application/json" -X GET http://localhost:8000/api/test → 观察是否返回 status=429 且 body 为 JSON。
用 Postman 连续快速点击 5 次同一接口,第 6 次应立刻收到含 retry_after 字段的 JSON 响应,而非等待 60 秒后才返回。
检查响应头:Content-Type 必须是 application/json;Retry-After 头应与 JSON 中 retry_after 数值一致。











