3

I'm using Spring (4.0.4) MVC to build a RESTful API. I need to return a ResponseEntity with a JSON representation of an error with http status 404.

However, when I make a request that results in a 404, instead of seeing the error JSON, I get the following output:

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL /project/api/resource/100 was not found on this server.</p>
<p>Additionally, a 503 Service Temporarily Unavailable
error was encountered while trying to use an ErrorDocument to handle the request.</p>
</body></html>

From this stackoverflow post, I've found that I can prevent this error page from being sent in place of the the error by including the following parameter in my web.xml

<init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
</init-param>

And using the a global advice similar to this:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    @ResponseBody
    public ResponseEntity<ErrorResponse> requestHandlingNoHandlerFound(HttpServletRequest req,
            NoHandlerFoundException ex) {

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setText("error response");
        return new ResponseEntity<ErrorResponse>(errorResponse, HttpStatus.BAD_REQUEST);
    }
}

However, when I add this MyExceptionHandler class, I am getting the following exception:

Caused by: java.lang.IllegalStateException: Ambiguous @ExceptionHandler method mapped for [class org.springframework.web.servlet.NoHandlerFoundException]: {public org.springframework.http.ResponseEntity com.rest.MyExceptionHandler.requestHandlingNoHandlerFound

The issue is that it doesn't know whether to call my custom ExceptionHandler or the one defined in web.servlet.

How can I resolve this issue so that I am able to return JSON from an API using Spring?

Community
  • 1
  • 1
greenJavaDev
  • 997
  • 4
  • 12
  • 24

2 Answers2

6

Here's what I think is happening:

Your @ControllerAdvice class extends ResponseEntityExceptionHandler which has a method handleNoHandlerFoundException (see at the end of the this file in the source code) Also, you have an @ExceptionHandler annotated method to handle the same exception.

@Override
ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex,
            HttpHeaders headers, HttpStatus status, WebRequest request)

I think having both of them in your code is causing this issue.

So either:

  1. Override that method (and don't use the @ExceptionHandler annotated method)
  2. do not extend ResponseEntityExceptionHandler. Use the @ExceptionHandler annotated method instead.

I think either of those options should work.

Jigish
  • 1,764
  • 1
  • 15
  • 20
  • Unfortunately neither solution 1 nor 2 resolved the problem I was having. I think the issue is that the ResponseEntityExceptionHandler class defines a final handler for the NoHandlerFoundException that can't be overridden. Does anyone have any other suggestions on how I can return error JSON with Spring REST or how to overcome the ambiguous @ExceptionHandler problem? – greenJavaDev Jun 10 '15 at 15:01
  • You're right. I wrote some sample code and was able to reproduce this issue. Are you using Spring Boot or is this a regular Spring MVC webapp? – Jigish Jun 12 '15 at 02:55
  • @Jigish's response worked great for me. I opted for solution 2, which worked well in my Spring Boot 1.5.6 app. No complaints from the framework, and my handler returned the error JSON that I was expecting. – user2337270 Nov 07 '17 at 20:41
-1

Try this.

package x.y.z;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

@ControllerAdvice
public class MyExceptionHandler {

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseStatus(value=HttpStatus.NOT_FOUND)
    @ResponseBody
    public ResponseEntity<String> handle404() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

        String json = "{\"error\":\"Resource not found.\"}";

        return new ResponseEntity<String>(json, headers, HttpStatus.NOT_FOUND);
    }
}
Bilal Mirza
  • 2,576
  • 4
  • 30
  • 57