How to get error list by using BindingResult for #Valid List - java

I have a Spring controller method that I wanna validate using #Valid and get the BindingResult errorlist. But In my #RequestBody have List list.
#PostMapping(path="/save/inouts")
public ResponseEntity<List<InoutResponse>> saveInouts(#Valid InoutWrapper inouts, BindingResults res){
.....
}
class InoutWrapper {
private List<Inouts> inoutList;
//getters and //setters
}
So I need to get error list as well as each error has the reference to Inout object to make InoutResponse.

You have 2 options, either remove the #valid annotation from the controller parameter and call the validation explicitly. Like below:
javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
final Set<ConstraintViolation<InoutWrapper>> constraints = validator.validate(inouts);
Or write an exception handler for your controller. I would prefer this one. Something like below:
#ControllerAdvice
class MyExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
// read ex.getBindingResult().
return super.handleMethodArgumentNotValid(ex, headers, status, request);
}
}

Related

How to handle a BindException thrown by #Valid annotation using a controller advice which extends ResponseEntityExceptionHandler?

I have a #NotNull validation for some fields in a POJO which represents my request params:
Endpoint:
public ResponseEntity<Void> initializeLaunchPost(#Valid OidcFlowInitRequest request) throws LtiValidationException {
Request Body
#RequiredArgsConstructor
#ToString
#Getter
public class OidcFlowInitRequest { //java record public record keyword
#NotNull
private final String iss;
#NotNull
private final String loginHint;
#NotNull
private final String targetLinkUri;
}
I'm performing a test to check the exception thrown:
#Test
void whenRequiredParamNotPassedReturnBadRequest() throws Exception {
MultiValueMap<String, String> wrongInitFlowRequestParams = new LinkedMultiValueMap<>(initFlowRequestParams);
wrongInitFlowRequestParams.remove("iss");
mockMvc.perform(post(OIDC_INIT_FLOW_ENDPOINT).contentType(MediaType.APPLICATION_FORM_URLENCODED).params(wrongInitFlowRequestParams))
.andExpect(status().isBadRequest()).andExpect(jsonPath("$.description", containsString("Mandatory request param")))
.andDo(print());
}
The problem is that the only way to execute my exception handler is overriding this method which is marked as deprecated and for removal:
#RestControllerAdvice
public class ControllerExceptionAdvice extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleBindException(BindException ex, HttpHeaders headers, HttpStatusCode status, WebRequest request) {
String field = ex.getFieldError().getField();
LtiErrorResponse body = new LtiErrorResponse(HttpStatus.BAD_REQUEST.value(),
"Mandatory request param <" + field + "> not present");
return handleExceptionInternal(ex, body, headers, status, request);
}
}
According to some post the test should be throwing MethodArgumentNotValid but if I implement that method is not executed. In fact, my test prints this in the console:
Resolved Exception:
Type = org.springframework.validation.BindException
So what other alternative do I have? My current solution it's not future proved.

Spring http request validation with anotations

I am trying to implement validation of incoming http request in spring boot app.
I was following these instructions:
https://www.yawintutor.com/how-to-validate-request-body-in-spring-boot/
but I don't get response like stated in article.
I am getting exception in my console though: DefaultHandlerExceptionResolver: ... DefaultMessageSourceResolvable: default message [must be a well-formed email address]]
Process is stopped based on set constraint (email formating, obviously), but I don't get http response messages, just Bad Request.
This is my controller:
#RestController
#Validated
#RequestMapping(path = "/user", produces = MediaType.APPLICATION_JSON_VALUE)
public class UserController {
#PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> createUser(#Valid #RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.ok(user);
}
}
And this is entity :
#Entity
#Builder
#Data
#NoArgsConstructor
#AllArgsConstructor
public class User {
#Email
private String email;
}
What am I missing?
First of all I'd like to mention that for me the provided link (https://www.yawintutor.com/how-to-validate-request-body-in-spring-boot/) didn't work somehow...
Now to your question. As I see it, in order to archive this you'd have to create a validator first and then you could continue with something line this (not your example but it should help):
#RequestMapping(value = "/validation", method = RequestMethod.POST)
public ResponseEntity<?> acceptData(#Valid #RequestBody Data data, Errors errors,
#RequestHeader(HttpHeaders.ACCEPT_LANGUAGE) String language) {
stringValueValidator.validate(language, data, errors);
if (errors.hasErrors()) {
return new ResponseEntity<>(createErrorString(errors), HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>(HttpStatus.OK);
}
Use #ControllerAdvice and create your own response Object for representing not valid arguments. Example below will return all not valid arguments with its messages.
#ControllerAdvice
public class ExceptionHandlerClass extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
List<String> validationList = ex.getBindingResult().getFieldErrors().stream().map(fieldError->fieldError.getDefaultMessage()).collect(Collectors.toList());
return new ResponseEntity<>(new ExceptionObject(false,validationList), status);
} }

Spring REStful web service #initbinder not allowing other validation

I have the following Rest controller:
#RestController
public class DocumentSearchController_global
{
#InitBinder//("TestCustomAnotation")
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new ChekAtleastOneValueValidator());
}
#RequestMapping(value = "/validator", method = RequestMethod.POST, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
protected DocumentSearchResponse validatortest(#Valid #RequestBody TestCustomAnotation objDMSRequest, Errors e, BindingResult br) throws AppException
{
if(br.hasErrors())
System.out.println("ERRor");
if (e.hasErrors())
{
System.out.println("Got Error: "+ e.getFieldError());
}
DocumentSearchResponse objDocSearchResponse = null;
return objDocSearchResponse;
}
#ExceptionHandler
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public String handleMethodArgumentNotValidException(
MethodArgumentNotValidException error) {
System.out.println("ERROR-->>>>>>>>>>>>>>>>>>>>>>>>" +error.getMessage());
return "Bad request: " + error.getMessage();
}
}
And this is the bean where the request will be cast:
public class TestCustomAnotation
{
#ValidDocumentModifiedDate({"7Days", "30Days","60Days"})
String docModifiedDate;
#NotNull
String objectId;
#NotNull
String jobId;
Setter and GEtter
}
In the controller if I specify binder.setValidator(new
ChekAtleastOneValueValidator()); the contol will only go to
ChekAtleastOneValueValidator it will not check for #notnull
#ValidDocumentModifiedDate`
If I don't have binder.setValidator(new
ChekAtleastOneValueValidator()); then the control will check for
#notnull#ValidDocumentModifiedDate validation but not
ChekAtleastOneValueValidator.
My question is: is there a way in Spring to use Spring validation, custom annotation and #notnull annotation and get all the error of all the validation or spring allows to use only Spring validators?
Actually the question itself was wrong. I got the answer I use a Spring Validator class to validate all the request comming in and then use #validated in stead of #valid. I don't use annotation at the request anymore and let the class be a POJO. thats it problem solved

MethodArgumentNotValidException not thrown

My controller looks like the following:
#RequestMapping(value = "/cars/{types}", method = RequestMethod.PUT,
headers = "Accept=application/json")
#ResponseStatus(HttpStatus.OK)
public void startEngine(
#PathVariable #Min(0) String types, #RequestBody #Valid someObject request, BindingResult result)
throws MethodArgumentNotValidException {
if(result.hasErrors())
{
System.out.println("Error");
//Should I be throwing MethodArgumentNotValidException here? And if so how? I don't know how to retrieve the first parameter for it's constructor (MethodParameter object)
}
//Controller code
}
So after I verify whether or not my result object encountered any errors during validation, how can I then throw the MethodArgumentNotValidException? Or should Spring be already throwing that exception during validation?
If I remember correctly, Spring should throw MethodArgumentNotValidException only if you have not provided an Errors (here, BindingResult) parameter for the #Valid annotated parameter.
You can throw it yourself if you would like to.

Spring MVC - how to map lookup path to the produces values

If this is a method in my controller:
#RequestMapping(method=RequestMethod.GET, produces={"application/json",
"application/xml"}, value="/myService")
#ResponseBody
public ResponseEntity myMethod(...) {
...
}
In a another class I have the lookup path /myService and I want to get the values in the produces list in the method above: application/json and application/xml. Is it possible to get access to the values in the produces list using some of Spring MVC's classes?
This peace of code might be doing what you want (or at least point you to the right direction):
#ControllerAdvice
public class DefaultControllerAdvice extends ResponseEntityExceptionHandler {
protected ResponseEntity<Object> handleHttpMediaTypeNotAcceptable(
HttpMediaTypeNotAcceptableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return handleExceptionInternal(ex, "Supported media types: " + ex.getSupportedMediaTypes(),
headers, status, request);
}
}
Key fact is that HttpMediaTypeNotAcceptableException contains supported media types initialized by handler mapper.

Categories

Resources