Spring+Tomcat中如何针对特定控制器限制POST请求JSON体大小
针对特定控制器/方法限制JSON请求体大小的实现方案
方案一:自定义注解 + AOP切面
通过自定义注解标记需要限制的方法,结合切面在请求到达业务逻辑前校验请求体大小。
- 定义自定义限制注解
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface MaxJsonBodySize { // 单位:字节,例如1MB = 1024*1024 = 1048576 long value(); }
- 编写切面类实现校验逻辑
@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(); } }
- 在目标控制器方法上标记注解
@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
相关产品推荐
相关产品推荐

