如何让Spring Boot控制器端点在接收含无效字符的请求时返回400 Bad Request而非500 Internal Server Error?
当然可以搞定这个问题!当URL里包含像%2500这类无效字符时,Spring Boot默认会把容器(比如Tomcat)抛出的异常转换成500内部错误,但我们可以通过几种方式调整,让它返回更符合语义的400 Bad Request。下面给你两种实用的方案:
方案一:自定义全局异常处理器
这种方法是捕获容器层抛出的非法URL异常,直接返回400状态码。以Tomcat为例,当请求URL包含无效字符时,它会抛出IllegalArgumentException(或者Tomcat特定的Http11InputBuffer.ParseException),我们可以通过全局异常处理器拦截这类异常:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class GlobalUrlExceptionHandler { // 处理Tomcat抛出的URL非法字符异常 @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity<String> handleInvalidUrlArgument(IllegalArgumentException ex) { // 判断异常信息是否匹配URL非法字符的场景 if (ex.getMessage().contains("Invalid character found in the request target")) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body("请求URL包含无效字符,请检查后重试"); } // 其他参数异常按原有逻辑处理 return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(ex.getMessage()); } // 捕获Tomcat特定的解析异常(部分Tomcat版本会抛出这个) @ExceptionHandler(org.apache.coyote.http11.Http11InputBuffer.ParseException.class) public ResponseEntity<String> handleTomcatUrlParseException(org.apache.coyote.http11.Http11InputBuffer.ParseException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body("请求格式无效,URL包含非法字符"); } }
这个方案的好处是不用修改容器配置,直接利用Spring的全局异常处理机制,覆盖默认的500返回逻辑。
方案二:自定义过滤器提前拦截
如果想在请求到达控制器之前就拦截掉非法URL,可以写一个自定义过滤器,提前检查URL中的无效字符:
import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.regex.Pattern; @Component public class UrlValidationFilter implements Filter { // 定义需要拦截的无效字符正则(这里匹配空字节的URL编码形式) private static final Pattern INVALID_URL_CHAR_PATTERN = Pattern.compile("%00|%2500", Pattern.CASE_INSENSITIVE); @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String requestUri = httpRequest.getRequestURI(); String queryString = httpRequest.getQueryString(); // 检查请求路径和查询参数中是否存在无效字符 boolean hasInvalidChar = INVALID_URL_CHAR_PATTERN.matcher(requestUri).find(); if (!hasInvalidChar && queryString != null) { hasInvalidChar = INVALID_URL_CHAR_PATTERN.matcher(queryString).find(); } if (hasInvalidChar) { httpResponse.setStatus(HttpServletResponse.SC_BAD_REQUEST); httpResponse.getWriter().write("请求URL包含无效字符,请检查后重试"); return; } // 没有问题,继续执行后续流程 chain.doFilter(request, response); } }
这个方案更灵活,你可以根据需求扩展正则表达式,拦截更多类型的非法字符。
注意事项
- 如果你用的是Tomcat以外的容器(比如Jetty、Undertow),抛出的异常类型会不同,需要调整异常处理器捕获的异常类。比如Jetty会抛出
org.eclipse.jetty.http.BadMessageException,Undertow则是io.undertow.server.handlers.form.FormParserException之类的异常。 - 不要随便使用
server.tomcat.relaxed-path-chars或server.tomcat.relaxed-query-chars配置,这些是用来允许某些特殊字符,而不是拒绝,可能带来安全风险。
内容的提问来源于stack exchange,提问作者Benjsoft
相关产品推荐
相关产品推荐

