Spring Boot如何捕获请求目标含非法字符的IllegalArgumentException?
问题原因
这个IllegalArgumentException是Tomcat在解析请求行阶段抛出的,此时请求还未进入Spring MVC的处理流程,所以你写的@ExceptionHandler根本无法捕获——Spring的异常处理器仅负责处理Spring上下文内抛出的异常。
解决方案
方案1:配置Tomcat允许特殊字符(不推荐,需注意安全风险)
如果业务场景确实需要接收包含\的请求参数,可以通过配置让Tomcat放松对请求字符的校验:
在application.properties中添加:
# 允许路径中的特殊字符 server.tomcat.relaxed-path-chars=\\ # 允许查询参数中的特殊字符 server.tomcat.relaxed-query-chars=\\
或者在application.yml中:
server: tomcat: relaxed-path-chars: '\\' relaxed-query-chars: '\\'
注意:这种做法会降低请求安全性,可能带来注入类风险,仅在明确业务需求且做好防护的情况下使用。
方案2:自定义Spring Boot全局错误处理
利用Spring Boot的错误处理机制,接管/error端点的响应,可捕获所有容器层面抛出的异常:
方式一:实现ErrorController
@RestController public class CustomErrorController implements ErrorController { private final ErrorAttributes errorAttributes; public CustomErrorController(ErrorAttributes errorAttributes) { this.errorAttributes = errorAttributes; } @RequestMapping("/error") public ResponseEntity<ApiResponseDTO> handleError(HttpServletRequest request) { Map<String, Object> errorDetails = errorAttributes.getErrorAttributes( new ServletWebRequest(request), ErrorAttributeOptions.defaults()); String errorMessage = (String) errorDetails.get("message"); HttpStatus status = HttpStatus.valueOf((Integer) errorDetails.get("status")); List<String> errors = List.of(errorMessage); ApiResponseDTO<?> response = ApiResponseDTO.builder() .status("ERROR") .errors(errors) .httpStatus(status) .timestamp(ZonedDateTime.now(ZoneId.of("Z"))) .build(); return new ResponseEntity<>(response, status); } }
方式二:使用@RestControllerAdvice配合ErrorAttributes
@RestControllerAdvice public class GlobalErrorHandler { private final ErrorAttributes errorAttributes; public GlobalErrorHandler(ErrorAttributes errorAttributes) { this.errorAttributes = errorAttributes; } @ExceptionHandler(Exception.class) public ResponseEntity<ApiResponseDTO> handleAllErrors(HttpServletRequest request) { ServletWebRequest webRequest = new ServletWebRequest(request); Map<String, Object> errorDetails = errorAttributes.getErrorAttributes( webRequest, ErrorAttributeOptions.of(ErrorAttributeOptions.Include.MESSAGE)); String message = (String) errorDetails.get("message"); HttpStatus status = HttpStatus.valueOf((Integer) errorDetails.get("status")); List<String> errors = List.of(message); ApiResponseDTO<?> response = ApiResponseDTO.builder() .status("ERROR") .errors(errors) .httpStatus(status) .timestamp(ZonedDateTime.now(ZoneId.of("Z"))) .build(); return new ResponseEntity<>(response, status); } }
这种方式可统一处理所有未被Spring MVC捕获的容器级异常,包括Tomcat抛出的请求解析错误。
方案3:自定义Tomcat Valve(容器层面拦截)
如果需要在Tomcat处理请求的最早期拦截异常,可以自定义一个Valve:
public class CustomRequestParseValve extends ValveBase { @Override public void invoke(Request request, Response response) throws IOException, ServletException { try { getNext().invoke(request, response); } catch (IllegalArgumentException e) { if (e.getMessage().contains("Invalid character found in the request target")) { response.setStatus(HttpServletResponse.SC_BAD_REQUEST); response.setContentType("application/json"); ApiResponseDTO<?> errorResponse = ApiResponseDTO.builder() .status("ERROR") .errors(List.of("请求包含非法字符,请检查URL格式")) .httpStatus(HttpStatus.BAD_REQUEST) .timestamp(ZonedDateTime.now(ZoneId.of("Z"))) .build(); ObjectMapper mapper = new ObjectMapper(); response.getWriter().write(mapper.writeValueAsString(errorResponse)); return; } throw e; } } }
然后在Spring Boot中注册这个Valve:
@Configuration public class TomcatConfig implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> { @Override public void customize(TomcatServletWebServerFactory factory) { factory.addEngineValves(new CustomRequestParseValve()); } }
这种方式能直接在Tomcat层面拦截异常并返回自定义响应。
内容的提问来源于stack exchange,提问作者Shanka Somasiri
相关产品推荐
相关产品推荐

