Spring-boot: @ResponseStatus is ignored if exception is thrown in filter on embedded Tomcat

Created on 28 May 2015  路  9Comments  路  Source: spring-projects/spring-boot

If throwing an exception annotated with @ResponseStatus from a controller or HandlerInterceptor the status of the HTTP response will be set according to the value in the annotation.

If throwing the same exception from a Filter (in my example a OncePerRequestFilter, but that shouldn't matter) the status of the HTTP response is always 500.

Source code (Groovy, Gradle) for a working demonstration can be found here.

When requesting /controller the controller throws an exception, when requesting /filter the filter throws an exception.

GET /controller

HTTP/1.1 400 Bad Request
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Thu, 28 May 2015 01:34:39 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked

{
"error": "Bad Request",
"exception": "boot.filter.CustomException",
"message": "Controller threw this exception",
"path": "/controller",
"status": 400,
"timestamp": 1432776879005
}

GET /filter

HTTP/1.1 500 Internal Server Error
Connection: close
Content-Type: application/json;charset=UTF-8
Date: Thu, 28 May 2015 01:36:13 GMT
Server: Apache-Coyote/1.1
Transfer-Encoding: chunked

{
"error": "Internal Server Error",
"exception": "boot.filter.CustomException",
"message": "Filter threw this exception",
"path": "/filter",
"status": 500,
"timestamp": 1432776973107
}

I've only tried this on Tomcat so I don't know if this applies to other containers as well.

Most helpful comment

Hi,

I have the same issue as the OP (but in Spring Boot with Embedded Tomcat): could the custom Exception thrown from a Filter return the custom HttpStatus code please.

The Http response :

        {
        "timestamp":1450074617553,
        "status":500,"error":"Internal Server Error",
        "exception":"MyAuthenticationException",
        "message":"Really should be 401 Unauthorized",
        "path":"/mypath"
        }
        public class MyFilter extends GenericFilterBean  {
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws AuthenticationException, IOException, ServletException {
                throw new MyAuthenticationException("Really should be 401 Unauthorized");
            }
        }
        @ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="Unauthorized")
        public class MyAuthenticationException extends AuthenticationException  {

            public MyAuthenticationException(String msg) {
                super(msg);
            }
        }

        @Configuration
        @EnableWebSecurity
        public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

            public MyWebSecurityConfig () {
                // Disable the default security configuration
                super(false);
            }

            @Configuration                                                   
            public static class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

                @Override
                protected void configure(HttpSecurity http) throws Exception {
                    http.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
                }
            }
        }

Thanks, in advance, and also for all the great work you do!

All 9 comments

See #2745

I'm not sure if these two are related.

This doesn't work in embedded Tomcat whereas #2745 seems to be working in embedded but not in standalone Tomcat.

I'm also not using any custom exception handlers.

Stepping through BasicErrorController with a debugger shows that it returns a HttpEntity with the error body and status of 500 when the filter throws the exception.

This is actually hinted at in the response since the error body says the status is 500 too.

I am not sure either but since it touches the same area (and the reverse environment) it's worth investigating them at the same time.

Encountered the same problem and Google brought me here. Logical workaround is using your own ErrorController. Only catch there is that the AbstractErrorController and the BasicErrorController are not easy to extended as you'll need the errorAttributes to get the Exception.

@RestController
@RequestMapping("/error")
public class ErrorController implements org.springframework.boot.autoconfigure.web.ErrorController {

  private final ErrorAttributes errorAttributes;

  @Autowired
  public ErrorController(ErrorAttributes errorAttributes) {
    Assert.notNull(errorAttributes, "ErrorAttributes must not be null");
    this.errorAttributes = errorAttributes;
  }

  @Override
  public String getErrorPath() {
    return "/error";
  }

  @RequestMapping
  public ResponseEntity<Map<String, Object>>  error(HttpServletRequest aRequest, HttpServletResponse response) {
    RequestAttributes requestAttributes = new ServletRequestAttributes(aRequest);
    Map<String, Object> result = this.errorAttributes.getErrorAttributes(requestAttributes, false);

    Throwable error = this.errorAttributes.getError(requestAttributes);

    ResponseStatus annotation = AnnotationUtils.getAnnotation(error.getClass(), ResponseStatus.class);
    HttpStatus statusCode = annotation != null ? annotation.value() : INTERNAL_SERVER_ERROR;
    return new ResponseEntity<>(result, statusCode) ;
  }

}

Hi,

I have the same issue as the OP (but in Spring Boot with Embedded Tomcat): could the custom Exception thrown from a Filter return the custom HttpStatus code please.

The Http response :

        {
        "timestamp":1450074617553,
        "status":500,"error":"Internal Server Error",
        "exception":"MyAuthenticationException",
        "message":"Really should be 401 Unauthorized",
        "path":"/mypath"
        }
        public class MyFilter extends GenericFilterBean  {
            @Override
            public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                    throws AuthenticationException, IOException, ServletException {
                throw new MyAuthenticationException("Really should be 401 Unauthorized");
            }
        }
        @ResponseStatus(value=HttpStatus.UNAUTHORIZED, reason="Unauthorized")
        public class MyAuthenticationException extends AuthenticationException  {

            public MyAuthenticationException(String msg) {
                super(msg);
            }
        }

        @Configuration
        @EnableWebSecurity
        public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

            public MyWebSecurityConfig () {
                // Disable the default security configuration
                super(false);
            }

            @Configuration                                                   
            public static class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

                @Override
                protected void configure(HttpSecurity http) throws Exception {
                    http.addFilterBefore(new MyFilter(), UsernamePasswordAuthenticationFilter.class);
                }
            }
        }

Thanks, in advance, and also for all the great work you do!

@mvcvm The @ResponseStatus annotation is only considered when the exception is thrown from within a Spring MVC call. Specifically the ResponseStatusExceptionResolver will be consulted by the DispatcherServlet in processHandlerException.

Since Filters operate at a higher level, and aren't aware of Spring MVC, you need to set the response status directly. Alternatively, you could look at using a HandlerInterceptor which is similar to a filter but specifically for Spring MVC. I think that exceptions thrown from a HandlerInterceptor will be processed.

@philwebb Thanks for the pointer. You are correct; my custom Exception is now returned to the client from my HandlerInterceptor. I have to use this approach now since I found that the request headers have not been fully populated in a filter inserted before UsernamePasswordAuthenticationFilter.

For anyone interested, here's my version of HandlerInterceptor that philwebb suggested.

@Component
public class MyInterceptor implements HandlerInterceptor {    
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {     
        try{
            if(!DoSomethingWith(request.getHeader("MyHeader")).getStatusCode().equals(HttpStatus.OK)){
                throw new MyException();
            }                       
        } catch (NullPointerException nullMyHeader){                
            throw new MyException();
        }
        return true;
    }
    // ... empty postHandle() and afterCompletion() omitted
}

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
          .addResourceHandler(...)
          .addResourceLocations(...);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor());
    }

    @Bean
    public MappedInterceptor myInterceptor() {
        return new MappedInterceptor(
                new String[] {"/my/path/**"},
                new MyInterceptor());
    }  
}

I was also helped by SPR-10655

@Raniz85 I think the comments above also apply to your original report. The fact that @ResponseStatus doesn't work with a Filter is by design.

Was this page helpful?
0 / 5 - 0 ratings