1

I am fairly new to Java/Spring domain and I am trying to utilize @ControllerAdvice in my Spring Boot application. The ControllerAdvice catches the exception but doesn't show my custom response.

following is the snap shot of RestController which is throwing the exception

ResponseEntity<MyClass> response = new ResponseEntity<MyClass>(HttpStatus.OK);

    try {   
        response = restTemplate.exchange(url, HttpMethod.GET, entity, MyClass.class);
    } catch (HttpClientErrorException  ex) {        
        throw ex;
    }catch (HttpServerErrorException   ex) {
        logger.debug(ex.getMessage() + " Server Status Code - " + ex.getStatusCode());
    }
    catch(Exception ex) {
        logger.debug(ex.getMessage() + " Generic Exception ");
    }

following is the @ControllerAdvice

@ControllerAdvice
public class GlobalExceptionHandler extends Exception {

    @ExceptionHandler(HttpClientErrorException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse errorHandle(HttpClientErrorException ex) {

          String responseBody = ex.getResponseBodyAsString(); 

          ObjectMapper mapper = new ObjectMapper();
          mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
          ErrorResponse errorResponse = new ErrorResponse();
          try { 
              errorResponse = mapper.readValue(responseBody, ErrorResponse.class); 

          } catch (JsonMappingException e) { 
          e.printStackTrace(); 
          } catch (JsonProcessingException e) { 
              e.printStackTrace(); 
          }
        return errorResponse;
    }
}

and other classes

@JsonIgnoreProperties
public class ErrorResponse {
    @JsonProperty("error")
    public Error error;

      @JsonProperty("version") 
      public String version;

}

public class Error {
    @JsonProperty("code")
    public String Code;

    @JsonProperty("message")
    public String Message;

    public String getCode() {
        return Code;
    }

    public void setCode(String code) {
        Code = code;
    }

    public String getMessage() {
        return Message;
    }

    public void setMessage(String message) {
        Message = message;
    }

}

the error I am getting when using POSTMAN is

{
    "timestamp": "2019-11-20T05:42:24.126+0000",
    "status": 404,
    "error": "Not Found",
    "message": "400 Bad Request",
    "path": "myApi/somecontroller"
}

and the error I get on browser is as follows

Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.

Tue Nov 19 23:40:26 CST 2019
There was an unexpected error (type=Not Found, status=404).
400 Bad Request

any idea/suggestion why this behavior? I was expecting an JSON representation of ErrorResponse class.

Update

enter image description here

Karan
  • 752
  • 2
  • 13
  • 34
  • Possible duplicate of [This application has no explicit mapping for /error](https://stackoverflow.com/questions/31134333/this-application-has-no-explicit-mapping-for-error) – Vinay Prajapati Nov 20 '19 at 05:56
  • Where have you defined @Controller? Check this out https://stackoverflow.com/questions/56648041/implementing-the-responseerrorhandler-interface/56648262#56648262 This might help you. – Nitin Zadage Nov 20 '19 at 06:38

3 Answers3

0

This isn't an Exception this is an Error scenario

Try to use following code.

@RestController
public class IndexController implements ErrorController{

    private static final String PATH = "/error";

    @RequestMapping(value = PATH)
    public String error() {
        return "Error handling";
    }

    @Override
    public String getErrorPath() {
        return PATH;
    }
}

Spring Boot default register BasicErrorController which needs to overriden

Swarit Agarwal
  • 2,520
  • 1
  • 26
  • 33
0

In your case, you are having a Controller Advice method that returns a Json response for the particular exception. So, there is no point in checking for the response in the browser ui as you need a proper error page to be configured through another exception handler for that to work as you desire. Otherwise the default spring white label error page will be shown.

For the rest exception handler , i think the JSON structure is not getting properly converted when you use the object mapper to create the instance.Try it like :

@ControllerAdvice
public class GlobalExceptionHandler extends Exception {

    @ExceptionHandler(HttpClientErrorException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ErrorResponse errorHandle(HttpClientErrorException ex) {

          String responseBody = ex.getResponseBodyAsString(); // The response body structure is different, you should get the message and stacktrace separately and assign it to your own response object
          ErrorResponse errorResponse = new ErrorResponse();
          Error error = new Error();
          error.setMessage(ex.getMessage());
          error.setCode(HttpStatus.BAD_REQUEST);
          errorResponse.setError(error);
          return errorResponse;
    }
}

getResponseBodyAsString - Return the response body converted to String. The charset used is that of the response "Content-Type" or otherwise "UTF-8".

Ananthapadmanabhan
  • 5,706
  • 6
  • 22
  • 39
  • Initially I also thought of doing that but the message in ex.getMessage() is just String "400 BAD REQUEST" but String responseBody = ex.getResponseBodyAsString() is giving proper Error object – Karan Nov 20 '19 at 14:35
  • @Karan could you please try to debug the method and ensure that the object mapper is populating the errorResponse object in the right way. – Ananthapadmanabhan Nov 20 '19 at 14:46
  • Yes, I checked this in debug mode again. At first I also thought that ex.getMessage() will give me entire object but it is not. – Karan Nov 20 '19 at 14:49
  • Added a screen-shot, please have a look – Karan Nov 20 '19 at 14:58
  • @Karan Do you have appropriate getters and setters for the ErrorResponse and Error objects to map all the fields ? – Ananthapadmanabhan Nov 20 '19 at 16:46
0

After doing some testing/search, figured this out. Using following Controller will be able to achieve that

@ControllerAdvice
public class GlobalExceptionHandler extends Exception {

    @ExceptionHandler(value = { HttpClientErrorException.class })
    public ResponseEntity<Object> handleResourceNotFoundException(HttpClientErrorException ex) {
        String responseBody = ex.getResponseBodyAsString();
        HttpHeaders headers = new HttpHeaders();

        headers.setContentType(MediaType.APPLICATION_JSON);     
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        ErrorResponse errorResponse = new ErrorResponse(); 
        try { 
            errorResponse = mapper.readValue(responseBody, ErrorResponse.class);         
        } catch (JsonMappingException e) { 
            e.printStackTrace(); 
            } 
        catch (JsonProcessingException e) { 
                e.printStackTrace(); 
            }

        ResponseEntity<Object> response = new ResponseEntity<>(errorResponse, headers, HttpStatus.BAD_REQUEST);

        return response;
    }
}
Karan
  • 752
  • 2
  • 13
  • 34