50

My Spring Boot application provides the following REST controller:

@RestController
@RequestMapping("/api/verify")
public class VerificationController {

    final VerificationService verificationService;

    Logger logger = LoggerFactory.getLogger(VerificationController.class);

    public VerificationController(VerificationService verificationService) {
        this.verificationService = verificationService;
    }

    @GetMapping
    public void verify(
            @RequestParam(value = "s1") String s1,
            @RequestParam(value = "s2") String s2) {     
        try {
            verificationService.validateFormat(s1, s2);
        } catch (InvalidFormatException e) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage());
        }
    }
}

In case validateFormat() throws the InvalidFormatException the client gets a HTTP 400 which is correct. The default JSON response body however looks like this:

{
    "timestamp": "2020-06-18T21:31:34.911+00:00",
    "status": 400,
    "error": "Bad Request",
    "message": "",
    "path": "/api/verify"
}

The message value is always empty even if I hard-code it like this:

throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "some string");

This is the exception class:

public class InvalidFormatException extends RuntimeException {

    public InvalidFormatException(String s1, String s2) {
        super(String.format("Invalid format: [s1: %s, s2: %s]", s1, s2));
    }
}
Robert Strauch
  • 12,055
  • 24
  • 120
  • 192
  • 1
    are you sure there is no custom interceptor? Spring ExceptionHandler does not convert the timestamp to a date format (it will be in millis). as we see in your response – Kumar V Jun 18 '20 at 22:24
  • @KumarV None that I'd be aware of. I used the Spring Intializr with IntelliJ which did most of the setup. How could I find this out? – Robert Strauch Jun 18 '20 at 22:54
  • which version of spring boot you are using? – Kumar V Jun 19 '20 at 02:39
  • Downgrade your version to 2.1.x and give it a try just to make sure if it is something to do with latest boot version – Kumar V Jun 19 '20 at 03:23
  • Other reason I could think is that the BAD_REQUEST status could be thrown by Spring for some other reason and not from your exception handling block. Debug with break point in your controller to make sure it's actually executing your validateFormat() method or throw ResponseStatusException with some other status code – Kumar V Jun 19 '20 at 03:59
  • I'm using `spring-boot-starter-parent:2.3.1.RELEASE`. Debugging shows that it steps correctly through the code. Also `e.getMessage()` contains the correct value. – Robert Strauch Jun 19 '20 at 06:35
  • Your guess was correct. After downgrading to `2.1.5.RELEASE` the `message` field is set with the correct value. Probably I'll need to check the Spring Boot release notes on this. – Robert Strauch Jun 19 '20 at 06:42

2 Answers2

91

This behavior has changed with Spring Boot 2.3 and is intentional. See release notes for details.

Setting server.error.include-message=always in the application.properties resolves this issue.

Robert Strauch
  • 12,055
  • 24
  • 120
  • 192
7

Setting server.error.include-message=always disclosures messages of internal exceptions and this might be a problem in production environment.

An alternative approach is to use ExceptionHandler. Here you can control what is transferred to client:

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ResponseStatusException.class)
    public ResponseEntity<String> handleBadRequestException(ResponseStatusException ex) {
        // if you want you can do some extra processing with message and status of an exception 
        // or you can return it without any processing like this:
        return new ResponseEntity<>(ex.getMessage(), ex.getStatus());
    }
}
michal.jakubeczy
  • 8,221
  • 1
  • 59
  • 63
  • It's often not good to expose exception messages in production. I'm surprised more people aren't taking that into consideration. – teuber789 Nov 09 '22 at 05:52