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

Spring Boot限流场景:响应提交后如何修改响应状态与响应体?

解决Filter限流器中捕获响应状态码并返回429的问题

核心思路是用自定义响应包装器捕获后端处理后的响应状态码,同时调整限流判断的执行顺序——先判断是否触发限流,再放行请求并根据响应结果更新限流计数。

1. 自定义响应包装器捕获状态码

需要覆盖所有可能设置响应状态码的方法,确保能准确捕获后端返回的状态:

public class StatusCapturingResponseWrapper extends HttpServletResponseWrapper {
    private int statusCode;

    public StatusCapturingResponseWrapper(HttpServletResponse response) {
        super(response);
    }

    @Override
    public void setStatus(int sc) {
        super.setStatus(sc);
        this.statusCode = sc;
    }

    @Override
    public void sendError(int sc) throws IOException {
        super.sendError(sc);
        this.statusCode = sc;
    }

    @Override
    public void sendError(int sc, String msg) throws IOException {
        super.sendError(sc, msg);
        this.statusCode = sc;
    }

    @Override
    public void setStatus(int sc, String sm) {
        super.setStatus(sc, sm);
        this.statusCode = sc;
    }

    public int getCapturedStatusCode() {
        // 未设置状态码时默认返回200
        return statusCode != 0 ? statusCode : HttpServletResponse.SC_OK;
    }
}

2. 实现限流器Filter(基于OncePerRequestFilter)

调整逻辑顺序:先判断是否触发限流,触发则直接返回429;未触发则放行请求,事后根据响应状态码更新限流计数(仅失败请求计入限流):

public class RateLimitFilter extends OncePerRequestFilter {
    // 示例:本地计数器,实际生产建议用Redis/Guava RateLimiter实现分布式限流
    private final AtomicInteger failRequestCount = new AtomicInteger(0);
    // 限流阈值:失败请求达到10次后触发限流
    private static final int LIMIT_THRESHOLD = 10;
    // 定义成功请求的状态码(可根据需求调整,比如包含201、204等)
    private static final int SUCCESS_STATUS = HttpServletResponse.SC_OK;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 先判断是否触发限流:达到阈值直接返回429
        if (failRequestCount.get() >= LIMIT_THRESHOLD) {
            response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":429,\"msg\":\"Too many failed requests, please try again later\"}");
            return;
        }

        // 包装响应,准备捕获状态码
        StatusCapturingResponseWrapper responseWrapper = new StatusCapturingResponseWrapper(response);
        // 放行请求,让后端业务逻辑处理
        filterChain.doFilter(request, responseWrapper);

        // 获取后端返回的状态码,仅失败请求计入限流计数
        int statusCode = responseWrapper.getCapturedStatusCode();
        if (statusCode != SUCCESS_STATUS) {
            failRequestCount.incrementAndGet();
        }
    }
}

关键注意事项

  • 必须覆盖所有设置状态码的方法:后端可能用setStatus、sendError等不同方法设置状态,包装器要全部捕获才能拿到正确的状态码
  • 限流判断要前置:如果在chain.doFilter之后再判断限流,此时响应已经提交,无法再修改状态码返回429
  • 异步场景适配:如果你的接口用了异步处理(AsyncContext),需要额外处理异步完成后的状态捕获逻辑,同步接口直接用上述方案即可

内容的提问来源于stack exchange,提问作者Euxinos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 03:35:19