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

Spring Boot 3中如何在Servlet Filter设置自定义响应?响应被覆盖

Spring Boot 3中Filter自定义错误消息不生效问题解决

问题场景

在Spring Boot 3中编写了如下Filter类,未调用chain.doFilter(),仅尝试返回自定义错误响应,但自定义消息始终不生效,返回的是Spring Boot默认错误JSON:

@Component
public class AcknowledgeFilter implements jakarta.servlet.Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 无效
        ((HttpServletResponse) response).sendError(500, "my-error-message");

        // 无效
        // response.getWriter().write("my-error-message");
        // response.getWriter().flush();
        // 或 response.getWriter().close();

        // 同样无效
        response.getOutputStream().write("my-error-message".getBytes());
        return;
    }
}

返回的默认响应:

{
  "timestamp": "2023-10-17T15:05:12.825+00:00",
  "status": 500,
  "error": "Internal Server Error",
  "path": "/api/myrequest/path"
}

开启调试日志后发现,错误响应被BasicErrorController处理:

o.s.security.web.FilterChainProxy        : Secured GET /api/order/last?customerNumber=123

o.a.c.c.C.[Tomcat].[localhost]           : Processing ErrorPage[errorCode=0, location=/api/error]
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
o.s.security.web.FilterChainProxy        : Securing GET /api/error?customerNumber=123
o.s.security.web.FilterChainProxy        : Secured GET /api/error?customerNumber=123
o.s.web.servlet.DispatcherServlet        : "ERROR" dispatch for GET "/api/error?customerNumber=123", parameters={masked}
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)
c.a.c.c.c.EndpointLoggingInterceptor     : Endpoint /api/error called
s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#error(HttpServletRequest)

默认响应来源

这个默认JSON响应来自Spring Boot自动配置的BasicErrorController。当调用sendError()方法,或者响应状态码被标记为错误码时,内嵌的Tomcat容器会触发错误页面转发,将请求转发到/error路径,而Spring Boot默认注册的BasicErrorController会处理这个请求,生成包含timestamp、status、error、path的默认错误JSON。

直接用getWriter()或getOutputStream()写响应无效,是因为Tomcat在触发错误页面转发时,会忽略之前写入的响应内容,重新发起对/error路径的请求,最终返回BasicErrorController生成的响应。

解决方法

方法一:直接写响应且不触发错误页面转发

如果想在Filter中直接返回自定义响应,需要避免触发Tomcat的错误页面机制。不要调用sendError(),而是直接设置响应状态码、内容类型,然后写入响应内容,同时确保响应已提交:

@Component
public class AcknowledgeFilter implements jakarta.servlet.Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        // 设置状态码
        httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        // 设置内容类型为JSON(根据需要调整)
        httpResponse.setContentType("application/json;charset=UTF-8");
        // 写入自定义响应内容
        String customError = "{\"status\":500,\"message\":\"my-error-message\"}";
        httpResponse.getWriter().write(customError);
        // 确保响应提交
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
        return;
    }
}

注意:这里用setStatus()而不是sendError(),setStatus()只会设置响应状态码,不会触发Tomcat的错误页面转发,因此写入的响应内容会被正常返回。

方法二:自定义ErrorController覆盖默认行为

如果希望保留Spring Boot的错误处理机制,但自定义响应内容,可以创建自定义的ErrorController来替代BasicErrorController:

  1. 实现ErrorController接口(或继承AbstractErrorController):
@RestController
public class CustomErrorController implements ErrorController {

    private final ErrorAttributes errorAttributes;

    public CustomErrorController(ErrorAttributes errorAttributes) {
        this.errorAttributes = errorAttributes;
    }

    @RequestMapping("/error")
    public ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request) {
        // 获取错误信息
        Map<String, Object> errorMap = errorAttributes.getErrorAttributes(request, ErrorAttributeOptions.defaults());
        // 修改或添加自定义字段
        errorMap.put("message", "my-error-message");

        HttpStatus status = HttpStatus.valueOf((Integer) errorMap.get("status"));
        return new ResponseEntity<>(errorMap, status);
    }
}
  1. 若需要获取调用sendError()时传入的自定义消息,可通过request.getAttribute(RequestDispatcher.ERROR_MESSAGE)获取:
@RequestMapping("/error")
public ResponseEntity<Map<String, Object>> handleError(HttpServletRequest request) {
    Map<String, Object> customResponse = new HashMap<>();
    Integer statusCode = (Integer) request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
    String errorMessage = (String) request.getAttribute(RequestDispatcher.ERROR_MESSAGE);

    customResponse.put("status", statusCode);
    customResponse.put("message", errorMessage != null ? errorMessage : "Default error message");
    customResponse.put("path", request.getRequestURI());

    HttpStatus status = HttpStatus.valueOf(statusCode);
    return new ResponseEntity<>(customResponse, status);
}

方法三:通过配置开启错误消息包含(针对sendError场景)

如果只是希望让sendError()传入的消息出现在默认响应中,可在application.properties或application.yml中添加配置:

server.error.include-message=always

这样BasicErrorController生成的响应会包含message字段,值就是调用sendError()时传入的内容。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 12:35:36