'Spring ResponseStatusException does not return reason
I have a very simple @RestController, and I'm trying to set a custom error message. But for some reason, the message for the error is not showing up.
This is my controller:
@RestController
@RequestMapping("openPharmacy")
public class OpenPharmacyController {
@PostMapping
public String findNumberOfSurgeries(@RequestBody String skuLockRequest) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "This postcode is not valid");
}
}
This is the response that I get:
{
"timestamp": "2020-06-24T17:44:20.194+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/openPharmacy/"
}
I'm passing a JSON, but I'm not validating anything, I'm just trying to set the custom message. If I change the status code, I see that on the response, but the message is always empty.
Why is this not working like expected? This is such a simple example that I can't see what may be missing. When I debug the code I can see that the error message has all the fields set. But for some reason, the message is never set on the response.
Solution 1:[1]
I have the very same issue. If I use this construct
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Error in update");
My message is not passed to client via JSON. For me, the only way to go around it was to create GlobalExceptionHandler class
package mypackage;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import java.util.Date;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ErrorDTO> generateNotFoundException(NotFoundException ex) {
ErrorDTO errorDTO = new ErrorDTO();
errorDTO.setMessage(ex.getMessage());
errorDTO.setStatus(String.valueOf(ex.getStatus().value()));
errorDTO.setTime(new Date().toString());
return new ResponseEntity<ErrorDTO>(errorDTO, ex.getStatus());
}
}
I have also created my own Exception type
package mypackage;
import org.springframework.http.HttpStatus;
public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
public HttpStatus getStatus() {
return HttpStatus.NOT_FOUND;
}
}
With this, I am able to throw exception from the controller and I am getting proper result in JSON - the message I want to see.
@PutMapping("/data/{id}")
public DataEntity updateData(@RequestBody DataEntity data, @PathVariable int id) {
throw new NotFoundException("Element not found");
}
I had to introduce ErrorDTO as well
package mypackage;
public class ErrorDTO {
public String status;
public String message;
public String time;
...
...
// getters and setters are here
...
...
}
Update
As mentioned by @Hassan and @cunhaf (in comments under original question), the solution with
server.error.include-message=always
works perfectly fine with ResponseStatusException. Still, solution with GlobalExceptionHandler might be better in case someone wants to pass more info via Exception.
Source code
Samples can be found here: Global Exception Handler
Solution 2:[2]
Strangely, Spring Boot 2.6.x changed this behavior again and the error message set on ResponseStatusException is not returned. I had to downgrade to 2.5.6 in order to solve it. In the end I had something like this:
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public MessageResponse deleteById(@PathVariable(value = "id") Integer id) {
try {
userService.deleteById(id);
} catch (Exception e) {
throw new ResponseStatusException(HttpStatus.EXPECTATION_FAILED, "Error deleting user. User has dependencies", e);
}
}
Solution 3:[3]
there is bean that can be overridden to include custom message.
Normal Spring Boot: org.springframework.boot.web.servlet.error.ErrorAttributes
Spring Wedbflux: org.springframework.boot.web.reactive.error.ErrorAttributes
the default implementation is DefaultErrorAttributes.
You can override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
the return make it return the message you want
In my case I created a decorator that will remove messages if it's internal server error:
public class CustomErrorAttributesDecorator implements ErrorAttributes {
private final ErrorAttributes errorAttributes;
CustomErrorAttributesDecorator(ErrorAttributes errorAttributes){
this.errorAttributes = errorAttributes;
}
@Override
public Map<String, Object> getErrorAttributes(ServerRequest request, ErrorAttributeOptions options) {
Map<String, Object> errorAttributesMap = this.errorAttributes.getErrorAttributes(request, options);
if(HttpStatus.INTERNAL_SERVER_ERROR.value() == (int) errorAttributesMap.get("status")){
errorAttributesMap.remove("message");
}
return errorAttributesMap;
}
...
}
and then I created a @Bean as follows:
@Bean
ErrorAttributes customErrorAttributes(){
return new CustomErrorAttributesDecorator(new DefaultErrorAttributes());
}
Solution 4:[4]
Starting from the 2.3 version, Spring Boot doesn't include an error message on the default error page. The reason is to reduce the risk of leaking information to a client
To change the default behavior, we can use a server.error.include-message property.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Digao |
| Solution 3 | |
| Solution 4 | Raj N |
