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

Spring+Tomcat中如何针对特定控制器限制POST请求JSON体大小

针对特定控制器/方法限制JSON请求体大小的实现方案

方案一:自定义注解 + AOP切面

通过自定义注解标记需要限制的方法,结合切面在请求到达业务逻辑前校验请求体大小。

  1. 定义自定义限制注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MaxJsonBodySize {
    // 单位:字节,例如1MB = 1024*1024 = 1048576
    long value();
}
  1. 编写切面类实现校验逻辑
@Aspect
@Component
public class JsonBodySizeLimitAspect {

    @Around("@annotation(maxJsonBodySize)")
    public Object checkBodySize(ProceedingJoinPoint joinPoint, MaxJsonBodySize maxJsonBodySize) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        
        // 仅处理POST类型的JSON请求
        if ("POST".equalsIgnoreCase(request.getMethod()) 
            && request.getContentType() != null 
            && request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
            
            long maxAllowedSize = maxJsonBodySize.value();
            long actualSize = request.getContentLengthLong();
            
            // 处理分块编码场景(getContentLengthLong返回-1)
            if (actualSize == -1) {
                ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
                actualSize = wrappedRequest.getContentAsByteArray().length;
                if (actualSize > maxAllowedSize) {
                    throw new IllegalArgumentException("JSON请求体超过限制:最大允许" + maxAllowedSize + "字节");
                }
                // 替换请求让后续流程正常读取
                RequestContextHolder.getRequestAttributes().setRequest(wrappedRequest);
            } else if (actualSize > maxAllowedSize) {
                throw new IllegalArgumentException("JSON请求体超过限制:最大允许" + maxAllowedSize + "字节");
            }
        }
        
        return joinPoint.proceed();
    }
}
  1. 在目标控制器方法上标记注解
@RestController
@RequestMapping("/demo")
public class DemoController {

    @PostMapping("/submit")
    @MaxJsonBodySize(value = 1048576) // 限制为1MB
    public ResponseEntity<String> handleSubmit(@RequestBody DemoDTO dto) {
        // 业务逻辑处理
        return ResponseEntity.ok("处理完成");
    }
}

方案二:自定义RequestBodyAdvice

利用Spring的请求体解析扩展机制,在JSON解析前校验请求体大小,适用于需要精准读取实际请求体长度的场景。

@ControllerAdvice
public class JsonBodySizeAdvice extends RequestBodyAdviceAdapter {

    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 仅对标记了@MaxJsonBodySize的方法生效,且仅处理JSON转换器
        return methodParameter.hasMethodAnnotation(MaxJsonBodySize.class)
                && MappingJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        MaxJsonBodySize limitAnnotation = parameter.getMethodAnnotation(MaxJsonBodySize.class);
        long maxSize = limitAnnotation.value();
        
        // 读取请求体并校验大小
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int readBytes;
        long totalBytes = 0;
        
        while ((readBytes = inputMessage.getBody().read(buffer)) != -1) {
            totalBytes += readBytes;
            if (totalBytes > maxSize) {
                throw new IOException("JSON请求体超过限制:最大允许" + maxSize + "字节");
            }
            byteStream.write(buffer, 0, readBytes);
        }
        
        // 重置输入流供后续JSON解析使用
        byte[] bodyBytes = byteStream.toByteArray();
        return new HttpInputMessage() {
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream(bodyBytes);
            }

            @Override
            public HttpHeaders getHeaders() {
                return inputMessage.getHeaders();
            }
        };
    }
}

方案三:自定义Servlet Filter

从Servlet层面拦截请求,针对特定路径或标记注解的方法做校验,适合更底层的控制场景。

@Component
public class JsonSizeLimitFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 仅处理POST类型的JSON请求
        if (!"POST".equalsIgnoreCase(request.getMethod()) 
            || request.getContentType() == null 
            || !request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
            filterChain.doFilter(request, response);
            return;
        }
        
        // 检查当前请求对应的方法是否有大小限制注解
        HandlerMethod handlerMethod = getCurrentHandlerMethod(request);
        if (handlerMethod != null) {
            MaxJsonBodySize limitAnnotation = handlerMethod.getMethodAnnotation(MaxJsonBodySize.class);
            if (limitAnnotation != null) {
                ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
                byte[] bodyBytes = wrappedRequest.getContentAsByteArray();
                
                if (bodyBytes.length > limitAnnotation.value()) {
                    response.setStatus(HttpStatus.BAD_REQUEST.value());
                    response.getWriter().write("JSON请求体超过限制:最大允许" + limitAnnotation.value() + "字节");
                    return;
                }
                
                filterChain.doFilter(wrappedRequest, response);
                return;
            }
        }
        
        filterChain.doFilter(request, response);
    }

    private HandlerMethod getCurrentHandlerMethod(HttpServletRequest request) {
        Object handler = request.getAttribute(DispatcherServlet.EXECUTION_ATTRIBUTE);
        return handler instanceof HandlerMethod ? (HandlerMethod) handler : null;
    }
}

注意事项

  • 若请求采用分块传输编码(Transfer-Encoding: chunked),request.getContentLengthLong()会返回-1,此时必须通过读取请求体流来判断实际大小,方案二、三可覆盖这种场景。
  • 建议配合全局异常处理器(@ExceptionHandler)统一处理校验抛出的异常,返回标准化错误响应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 01:30:02