18

I got a working spring boot rest service. When the path is wrong it doesn't return anything. No response At all. At the same time it doesn't throw error either. Ideally I expected a 404 not found error.

I got a GlobalErrorHandler

@ControllerAdvice
public class GlobalErrorHandler extends ResponseEntityExceptionHandler {

}

There is this method in ResponseEntityExceptionHandler

protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
                                                     HttpStatus status, WebRequest request) {

    return handleExceptionInternal(ex, null, headers, status, request);
}

I have marked error.whitelabel.enabled=false in my properties

What else must I do for this service to throw a 404 not found response back to clients

I referred a lot of threads and don't see this trouble faced by anybody.

This is my main application class

 @EnableAutoConfiguration // Sprint Boot Auto Configuration
@ComponentScan(basePackages = "com.xxxx")
@EnableJpaRepositories("com.xxxxxxxx") // To segregate MongoDB
                                                        // and JPA repositories.
                                                        // Otherwise not needed.
@EnableSwagger // auto generation of API docs
@SpringBootApplication
@EnableAspectJAutoProxy
@EnableConfigurationProperties

public class Application extends SpringBootServletInitializer {

    private static Class<Application> appClass = Application.class;

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(appClass).properties(getProperties());

    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public FilterRegistrationBean correlationHeaderFilter() {
        FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
        filterRegBean.setFilter(new CorrelationHeaderFilter());
        filterRegBean.setUrlPatterns(Arrays.asList("/*"));

        return filterRegBean;
    }

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    static Properties getProperties() {
        Properties props = new Properties();
        props.put("spring.config.location", "classpath:/");
        return props;
    }

    @Bean
    public WebMvcConfigurerAdapter webMvcConfigurerAdapter() {
        WebMvcConfigurerAdapter webMvcConfigurerAdapter = new WebMvcConfigurerAdapter() {
            @Override
            public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
                configurer.favorPathExtension(false).favorParameter(true).parameterName("media-type")
                        .ignoreAcceptHeader(false).useJaf(false).defaultContentType(MediaType.APPLICATION_JSON)
                        .mediaType("xml", MediaType.APPLICATION_XML).mediaType("json", MediaType.APPLICATION_JSON);
            }
        };
        return webMvcConfigurerAdapter;
    }

    @Bean
    public RequestMappingHandlerMapping defaultAnnotationHandlerMapping() {
        RequestMappingHandlerMapping bean = new RequestMappingHandlerMapping();
        bean.setUseSuffixPatternMatch(false);
        return bean;
    }
}
buræquete
  • 14,226
  • 4
  • 44
  • 89
juniorbansal
  • 1,249
  • 8
  • 31
  • 51

3 Answers3

34

The solution is pretty easy:

First you need to implement the controller that will handle all error cases. This controller must have @ControllerAdvice -- required to define @ExceptionHandler that apply to all @RequestMappings.

@ControllerAdvice
public class ExceptionHandlerController {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value= HttpStatus.NOT_FOUND)
    @ResponseBody
    public ErrorResponse requestHandlingNoHandlerFound() {
        return new ErrorResponse("custom_404", "message for 404 error code");
    }
}

Provide exception you want to override response in @ExceptionHandler. NoHandlerFoundException is an exception that will be generated when Spring will not be able to delegate request (404 case). You also can specify Throwable to override any exceptions.

Second you need to tell Spring to throw exception in case of 404 (could not resolve handler):

@SpringBootApplication
@EnableWebMvc
public class Application {

    public static void main(String[] args) {
        ApplicationContext ctx = SpringApplication.run(Application.class, args);

        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }
}

Result when I use non defined URL

{
    "errorCode": "custom_404",
    "errorMessage": "message for 404 error code"
}

UPDATE: In case you configure your SpringBoot application using application.properties then you need to add the following properties instead of configuring DispatcherServlet in main method (thanks to @mengchengfeng):

spring.mvc.throw-exception-if-no-handler-found=true
spring.web.resources.add-mappings=false
Ilya Ovesnov
  • 4,079
  • 1
  • 27
  • 31
  • Thank you. Even after doing this change i am still not seeing no response. Please help!!! – juniorbansal Apr 20 '16 at 16:24
  • Can you post your filter implementation? CorrelationHeaderFilter will handle every request and might be the reason why it doesn't work for you. I tested with a code very close to yours and it working if I comment filter, add '@EnableWebMvc', and comment not needed annotation for it like '@EnableSwagger', '@@EnableJpaRepositories', '@@EnableAspectJAutoProxy'. – Ilya Ovesnov Apr 21 '16 at 04:32
  • I can post simple application to github if you want – Ilya Ovesnov Apr 21 '16 at 04:34
  • 18
    In case anybody else runs into problems with the code example, the custom ControllerAdvice worked for my **Spring Boot** application when I configured the dispatcher servlet using properties as opposed to inside of main: `spring.mvc.throw-exception-if-no-handler-found=true spring.resources.add-mappings=false` After I removed the dispatcherServlet configuration in main and used the above two properties in application.properties, errors were properly routed to my ControllerAdvice. Hope this helps somebody! – mengchengfeng Feb 14 '17 at 02:10
  • 1
    Please post the ErrorResponse class to make this answer complete. – chrisinmtown Jul 11 '17 at 12:58
  • When I add ErrorResponse class as a model, I cant get my custom 404 error message. error log: `No converter found for return value of type: class co.x.model.ErrorResponse ` can you help me ? – ACAkgul Oct 08 '17 at 12:07
  • @AhmetCanAkgül Make sure that you have `jackson-databind` is in your dependencies and that your ErrorResponse and nested classes has getters and setters: https://stackoverflow.com/questions/32905917/how-to-return-json-data-from-spring-controller-using-responsebody or https://stackoverflow.com/questions/33832735/spring-boot-application-no-converter-found-for-return-value-of-type – Ilya Ovesnov Oct 09 '17 at 16:09
  • this is further improved in spring boot latest versions – GvSharma Jan 28 '21 at 17:24
  • 2
    This really helped but replaced `spring.resources.add-mappings=false` which is deprecated by `spring.web.resources.add-mappings=false` in Spring boot 2+. Thanks. – Mohammed farhan May 28 '21 at 12:16
0

I know this is an old question but here is another way to configure the DispatcherServlet in code but not in the main class. You can use a separate @Configuration class:

@EnableWebMvc
@Configuration
public class ExceptionHandlingConfig {

    @Autowired
    private DispatcherServlet dispatcherServlet;

    @PostConstruct
    private void configureDispatcherServlet() {
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    }
}

Please not that this does not work without the @EnableWebMvc annotation.

Alternatic
  • 29
  • 5
0
  1. Add this to your Properties file.

     spring:
        mvc:
          throw-exception-if-no-handler-found: true
        web:
          resources:
            add-mappings: false
    
  2. In your @ControllerAdvice class add this:

    @ExceptionHandler(NoHandlerFoundException.class)
       public ResponseEntity<Object> handleNoHandlerFound404() {
       return new ResponseEntity<>(HttpStatus.BAD_REQUEST);;
    }
    
Zahid Khan
  • 2,130
  • 2
  • 18
  • 31