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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 07:55:46