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

含%符号的URL无法触发Spring Boot自定义错误页面问题

问题描述

请求处理过程中发生错误时,能正常获取自定义错误页面响应,但当请求URL末尾带有%符号(如http://localhost:8080/some-path%)时,返回的是Tomcat默认的HTTP 400错误页面。

使用Spring Boot v2.7.2及web starter,自定义ErrorController代码如下:

@Controller
public class CustomErrorController implements ErrorController {

    @RequestMapping("/error")
    public String handleError() {
        return "error";
    }
}

application.yml配置:

spring:
  web:
    resources:
      add-mappings: false
  mvc:
    throw-exception-if-no-handler-found: true

server:
  error:
    path: /error
    whitelabel:
      enabled: false

经排查,错误发生在org.apache.catalina.connector.CoyoteAdapter#postParseRequest方法的URI解码阶段:

// URI decoding
// %xx decoding of the URL
try {
    req.getURLDecoder().convert(decodedURI.getByteChunk(), connector.getEncodedSolidusHandlingInternal());
} catch (IOException ioe) {
    response.sendError(400, "Invalid URI: " + ioe.getMessage());
}

疑问:为何自定义错误响应未生效?能否修改该默认响应?

原因分析

核心原因是错误触发时机早于Spring MVC的请求处理流程。

当URL末尾带单独的%时,Tomcat在请求解析的早期阶段(CoyoteAdapter#postParseRequest)就会执行URI解码操作。单独的%属于非法URL编码(规范要求%后必须跟两位十六进制字符),此时Tomcat会直接调用response.sendError(400)返回错误,整个过程完全绕过了Spring的DispatcherServlet,因此自定义的ErrorController根本不会被触发。

解决方法

1. 配置Tomcat错误页面映射

通过Spring Boot配置让Tomcat在返回400错误时转发到自定义的/error路径,这样就能被你的ErrorController处理。创建配置类:

@Configuration
public class TomcatConfig {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        // 为400错误添加自定义页面映射
        factory.addErrorPages(new ErrorPage(HttpStatus.BAD_REQUEST, "/error"));
        return factory;
    }
}

2. 自定义Tomcat ErrorValve

如果需要更灵活的错误逻辑控制,可以自定义一个Valve替换Tomcat默认的错误处理阀,在里面拦截并处理URI解码导致的400错误:

首先实现自定义Valve:

public class CustomErrorValve extends ErrorReportValve {

    @Override
    protected void report(Request request, Response response, Throwable throwable) {
        // 判断是否是URI解码失败导致的400错误
        if (response.getStatus() == HttpStatus.BAD_REQUEST.value() && throwable instanceof IOException) {
            try {
                // 转发到自定义错误页面
                request.getRequestDispatcher("/error").forward(request, response);
            } catch (ServletException | IOException e) {
                // 转发失败时 fallback 到默认处理
                super.report(request, response, throwable);
            }
        } else {
            // 其他错误沿用默认处理逻辑
            super.report(request, response, throwable);
        }
    }
}

然后在配置类中注册这个Valve:

@Configuration
public class TomcatConfig {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.addContextCustomizers(context -> {
            // 将自定义Valve添加到Tomcat的处理管道中
            context.getPipeline().addValve(new CustomErrorValve());
        });
        return factory;
    }
}

3. 调整Tomcat URI解码规则(不推荐)

可以通过配置放宽Tomcat对URI字符的限制,避免因单独%触发400错误,但这种方式可能违反URL规范,带来安全风险,不建议生产环境使用。在application.yml中添加:

server:
  tomcat:
    relaxed-path-chars: "'{\"}[]|"
    relaxed-query-chars: "'{\"}[]|"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 02:02:21