2

I have a problem with Spring's exception handling for controllers. I have a class annotated with @RestControllerAdvice with a couple of @ExceptionHandler's, like this:

@ExceptionHandler(HttpRequestMethodNotSupportedException::class)
fun methodNotSupportedException(
    exception: HttpRequestMethodNotSupportedException,
    request: HttpServletRequest
): ResponseEntity<ApiError> {
    logger().error("Method not supported: {}", exception.message)
    val methodNotAllowed = HttpStatus.METHOD_NOT_ALLOWED
    val apiError = logAndBuildApiError(request, methodNotAllowed, exception)
    return ResponseEntity(apiError, methodNotAllowed)
}

and they work perfectly fine. In this case, when I'm trying to use an non-implemented HTTP method like POST:

{
    "requestUri": "/api/v1/items",
    "status": 405,
    "statusText": "Method Not Allowed",
    "createdAt": "2023-01-12T16:50:36.55422+02:00",
    "errorMessage": "Request method 'POST' not supported"
}

What I would like to achieve is to handle situations when someone is trying to reach an non-existing endpoint, i.e. the correct one is GET http://localhost:8080/api/v1/items.

But when I'm trying to reach http://localhost:8080/api/v1/itemss, which is of course nonexistent, I recieve a regular Spring whitelabel error page, but I would like to receive a JSON like in the former example:

{
    "requestUri": "/api/v1/itemss",
    "status": 404,
    "statusText": "Not Found",
    "createdAt": "2023-01-12T16:52:06.932108+02:00",
    "errorMessage": "Some error message"
}

How do I implement a @ExceptionHandler so it could handle exceptions related to non-existing resources?

hc0re
  • 1,806
  • 2
  • 26
  • 61
  • Does this answer your question? [Spring Boot and custom 404 error page](https://stackoverflow.com/questions/37398385/spring-boot-and-custom-404-error-page) – Murat Karagöz Jan 12 '23 at 15:00
  • https://www.baeldung.com/spring-boot-custom-error-page – Murat Karagöz Jan 12 '23 at 15:01
  • Have you tried `@ControllerAdvice` as this doesn't really match an `@RestController` but anything. You would also need an exception handler for a `NoHandlerFoundException` – M. Deinum Jan 12 '23 at 15:12
  • @MuratKaragöz I've seen this question, no, this doesn't help me, sadly. – hc0re Jan 12 '23 at 15:19
  • @M.Deinum changing from RestControllerAdvice to ControllerAdvice doesn't help – hc0re Jan 12 '23 at 15:20
  • As mentioned you need one for `NoHandlerFoundException` and set `spring.mvc.throw-exception-if-no-handler-found` to `true` else the `DispatcherServlet` will handle it itself. – M. Deinum Jan 12 '23 at 15:30
  • @M.Deinum I have set pring.mvc.throw-exception-if-no-handler-found to true, the logs look fine, but I still receive the whitelabel error page instead of a custom JSON... – hc0re Jan 12 '23 at 15:36

1 Answers1

1

spring.mvc.throw-exception-if-no-handler-found works in conjunction with spring.mvc.static-path-pattern. By default, the static path pattern is /**, which includes the whitelabel error pages that you're seeing.

See https://github.com/spring-projects/spring-boot/pull/31660 and https://gitter.im/spring-projects/spring-boot?at=62ba1378568c2c30d30790af and https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#web.servlet.spring-mvc.static-content

Option one is to set these two properties in your configuration.

spring:
  mvc:
    throw-exception-if-no-handler-found: true
    static-path-pattern: /static

Option 2 is to add @EnableWebMvc to your spring boot application, and set the spring.mvc.throw-exception-if-no-handler-found property to true. By adding EnableWebMvc you'll be getting the WebMvcConfigurationSupport bean, which will cause Spring not to initialize the WebMvcAutoConfiguration and thereby not set the static-path-pattern.

lane.maxwell
  • 5,002
  • 1
  • 20
  • 30
  • Don't add `@EnableWebMvc` (it will also render that property useless) but will disable a lot more then only the static resources. – M. Deinum Jan 12 '23 at 18:40
  • See the comment by @M.Deinum. There are side effects to using `@EnableWebMvc` that you may encounter. e.g. if you're controllers are annotated with `@Controller` instead of `@RestController`, they will not get handlers registered for their mappings. – lane.maxwell Jan 12 '23 at 19:47
  • That isn't a side effect, in fact the `@Controller` will work just fine. You won't get pre-configured jackson, the jackson you have will not be integrated into other parts of the framework, no exception handling etc. etc. – M. Deinum Jan 13 '23 at 08:29
  • @M.Deinum Are you certain that Controllers will work just fine? I have a vanilla spring boot api with RestControllers. If I change them to `@Controller` and add `@EnableWebMvc`, the handlers are not registered. If I drop `@EnableWebMvc` and leave `@Controller`, they work as expected. I agree though, `@EnableWebMvc` doesn't play nicely with spring boot. – lane.maxwell Jan 13 '23 at 15:14
  • Yes, as an `@RestController` itself is also an `@Controller` but just with an added `@ResponseBody` by default for the return types. If you have an `@Controller` with `@EnableWebMvc` you manually need to define the view resolvers etc. without those (and not using `@ResponseBody`) they won't resolve any views. – M. Deinum Jan 16 '23 at 07:08