I am not getting error message when validation fails in spring boot hibernate validation - spring

I have created a simple Spring Boot application and I want to apply hibernate validation. But I don't get errors message. I get only below message if validation fails
{
"timestamp": "2020-06-11T09:51:55.695+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/users/"
}
my domain code is below
#NotNull
#Size(min = 8, max = 16)
private String password;
my controller
#PostMapping(consumes = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE }, produces = {
MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<UserRest> createUser(#Valid #RequestBody UserDetailsRequestModel userDetails) {
UserRest userRest = new UserRest();
userRest.setFirstName(userDetails.getFirstName());
userRest.setLastName(userDetails.getLastName());
userRest.setEmail(userDetails.getEmail());
return new ResponseEntity<UserRest>(userRest, HttpStatus.OK);
}

Related

How I can localized error messages for Spring Validation errors, like #PathVariable #Min(1), #Email etc, if I use #ControllerAdvice?

For example:
#GetMapping("/exception/{id}")
public ResponseEntity getException(#PathVariable #Min(1) Integer id) {
return ResponseEntity.ok().body(id.toString());
}
it must returns "must be greater than or equal to 1" (default message) when I call this function with id=-5 from the eng page. And something like this "doit être supérieur ou égal à 1", when i coll it from french page.
hibernate-validator jar file contains different localization messages. You need to configure based on Accept-Language header in the request it will decide which message to be displayed.
#Bean
public AcceptHeaderLocaleResolver localeResolver() {
final AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
resolver.setDefaultLocale(Locale.US);
return resolver;
}
The Response DTO object.
#Data
public class ErrorDto {
private LocalDateTime timestamp;
private HttpStatus status;
private String error;
public ErrorDto(HttpStatus status, String error) {
timestamp = LocalDateTime.now();
this.status = status;
this.error = error;
}
}
Then in ControllerAdvice catch ConstraintViolationException and show a response.
#ResponseStatus(code = HttpStatus.BAD_REQUEST)
#ExceptionHandler({ConstraintViolationException.class})
public ResponseEntity<Object> handleConstraintViolation(ConstraintViolationException ex, WebRequest request) {
String error = "";
for (ConstraintViolation<?> violation : ex.getConstraintViolations()) {
error = violation.getMessage();
}
ErrorDto errorDto = new ErrorDto(HttpStatus.BAD_REQUEST, error);
return new ResponseEntity<>(errorDto, errorDto.getStatus());
}
It will give you response for given controller.
{
"timestamp": "2022-10-23T21:08:07.0459025",
"status": "BAD_REQUEST",
"error": "doit être supérieur ou égal à 1"
}

with #SpringBootTest (or #WebfluxTest), the errors attribute disappears

I made very simple controller like below.
#PostMapping("/books")
public void create(#Valid #RequestBody BookPayload bookPayload) {
}
#Getter
#Setter
public class BookPayload {
#NotBlank
private String name;
#NotBlank
private String author;
}
When I call this api without name. It responses like below.
{
"timestamp": "2022-03-26T14:06:43.564+00:00",
"path": "/books",
"status": 400,
"error": "Bad Request",
"requestId": "654248ee-5",
"errors": [
{
"codes": [
"NotBlank.bookPayload.name",
"NotBlank.name",
"NotBlank.java.lang.String",
"NotBlank"
],
"arguments": [
{
"codes": [
"bookPayload.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
}
],
... omit ...
}
]
}
You can see errors attribute in the response body.
But If I test this api with #SpringBootTest or #WebfluxTest, There is no errors attribute.
#Slf4j
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CommonErrorResponseTest {
private final WebClient web;
public CommonErrorResponseTest(#LocalServerPort Integer port) {
web = WebClient.create("http://localhost:" + port);
}
#Test
void _400_badRequest_violation() {
BookPayload bookPayload = new BookPayload();
bookPayload.setAuthor("John");
Mono<String> stringMono = web.post().uri("/books")
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.bodyValue(bookPayload)
.exchangeToMono(response -> response.bodyToMono(String.class));
String body = stringMono.block();
log.info("body: {}", body);
}
}
console
body: {"timestamp":"2022-03-26T14:19:21.981+00:00","path":"/books","status":400,"error":"Bad Request","requestId":"68df2a79-1"}
I'd like to know why I'm getting different results.
Spring Boot’s DevTools enables the inclusion of binding errors in the error response to ease problem solving during development. You can configure the same behaviour in your tests by setting server.error.include-binding-errors to always.
You can see a complete list of the properties that DevTools sets in the reference documentation.

Mandatory Validation for pathVariable not working

Currently we are adding mandatory validation for pathVariable - having customized message in response.
When #PathVariable is removed, then it is giving 404 error and not going into any of the exceptionHandler. Can you please help to resolve this issue?
#PostMapping(path = "{id}")
public ResponseEntity<String> migrate(#PathVariable(value = "id")
#Size(min = 1, max = 64, message = INVALID_FIELD_LENGTH_DETAILS)
String id) {
...
}
Error response as below:
{
"timestamp": "2022-02-08T15:26:58.649+00:00",
"status": 404,
"error": "Not Found",
"message": "",
"path": "/migrations"
}
javax.validation annotations are not supported yet: https://github.com/spring-projects/spring-framework/issues/11041
You can try several workarounds, e.g.:
#PostMapping(path = {"", "/{id}"})
public ResponseEntity<String> migrate(#PathVariable(value = "id", required = false)
Optional<String> id) {
if (!id.isPresent() && id.get().length() > 64) {
return new ResponseEntity<>("Validation error", HttpStatus.BAD_REQUEST);
} else {
return new ResponseEntity<>(id.orElse(""), HttpStatus.OK);
}
}

Spring Boot #Valid doesn't display message from #NotBlank

Why message from #NotBlank is not displayed?
Controller API:
#PostMapping("/create-folder")
public SuccessResponse createFolder(Principal principal, #Valid #RequestBody CreateFolderRequest request) {
return historyService.createFolder(principal.getName(), request.getFolderName());
}
Request Body:
#Data
public class CreateFolderRequest {
#NotBlank(message = "Folder name is mandatory.")
private String folderName;
}
JSON Response:
{
"timestamp": "2020-11-18T11:24:19.769+00:00",
"status": 400,
"error": "Bad Request",
"message": "Validation failed for object='createFolderRequest'. Error count: 1",
"path": "/api/history/create-folder"
}
Packages:
Valid:
import javax.validation.Valid;
NotBlank:
import javax.validation.constraints.NotBlank;
There is no global exception handlers in the project.
#Valid throw an exception of MethodArgumentNotValidException you massege in #NotBlank is get throw inside exception detail which isn't return to the customer. you need to extract the messages so try adding this method to the controller.
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public Map<String, String> handleValidationExceptions(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
above code read all errors inside the exception then get thier detail (filedName - errorMessage) and put them in list and then return the list to the clinet with 400 bad request status

How to wrap Path Not Found Exception in Spring Boot Rest using ExceptionHandler?

I am using Spring Boot and Spring Rest Example. In this example, I am passing custom header, if that value is valid, endpoint gets called successfully, if custom header value is not correct then I get below response, which I want to wrap into show it to the enduser using #ControllerAdvice ExceptionHandler.
Note: I went through Spring mvc - How to map all wrong request mapping to a single method, but here in my case I am taking decision based on CustomHeader.
{
"timestamp": "2020-01-28T13:47:16.201+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/employee-data/employee-codes"
}
Controller
#Operation(summary = "Find Employee")
#ApiResponses(value = { #ApiResponse(code = 200, message = "SUCCESS"),
#ApiResponse(code = 500, message = "Internal Server Error") })
#Parameter(in = ParameterIn.HEADER, description = "X-Accept-Version", name = "X-Accept-Version",
content = #Content(schema = #Schema(type = "string", defaultValue = "v1",
allowableValues = {HeaderConst.V1}, implementation = Country.class)))
#GetMapping(value = "/employees/employee-codes", headers = "X-Accept-Version=v1")
public ResponseEntity<Employees> findEmployees(
#RequestParam(required = false) String employeeCd,
#RequestParam(required = false) String firstName,
#RequestParam(required = false) Integer lastName) {
Employees response = employeeService.getEmployees(employeeCd, firstName, lastName);
return new ResponseEntity<>(response, HttpStatus.OK);
}
I've implemented HttpMessageNotReadableException and HttpMediaTypeNotSupportedException and NoHandlerFoundException, but still not able to wrap this error.
Any suggestions?
I was able to find the solution for it.
# Whether a "NoHandlerFoundException" should be thrown if no Handler was found to process a request.
spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false
Error Handling Code:
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
// custom logic here
return handleExceptionInternal(ex, error, getHeaders(), HttpStatus.BAD_REQUEST, request);
}
If you're using #ControllerAdvice,
do this:
#ControllerAdvice
public class RestResponseEntityExceptionHandler
extends ResponseEntityExceptionHandler {
#ExceptionHandler(value
= { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(
RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}

Resources