Getting value of invalid field after MethodArgumentNotValidException - spring

My entity class is annotated with #NotNull of hibernate for one of the field.I am catching the exception in #ExceptionHelper. My problem is that i want to access the value of the invalid field so that i can add it into the logger.
Here is the ExceptionHandler:
#ExceptionHandler({ MethodArgumentNotValidException .class })
#ResponseBody
public ResponseEntity<?> handleInvalidMethodArgException(MethodArgumentNotValidException de) {
List<FieldError> bindingResult = de.getBindingResult().getFieldErrors();
LOGGER.debug(String.format("Start : %s : handleDataexception()", this
.getClass().getSimpleName()));
for(FieldError fieldError: bindingResult){
LOGGER.debug("Invalid {} value submitted for {}",
fieldError.getRejectedValue(), fieldError.getField());
}
ErrorMsg = new ErrorMsg(errorCode, errorMessage);
return new ResponseEntity<>(errMsg, HttpStatus.NOT_FOUND);
}
I have added #Validated with the #RequestBody in the controller.

MethodArgumentNotValidException has a getBindingResult method that returns the BindingResult if the failure is validation-related, or null if none. So, you can call the getFieldErrors method which returns List of FieldErrors. Each FieldError has a getRejectedValue which gives you the invalid value of the corresponding field:
ex.getBindingResult().getFieldErrors().forEach(fieldError -> {
LOGGER.debug("Invalid {} value submitted for {}",
fieldError.getRejectedValue(), fieldError.getField());
});
ex is an instance of MethodArgumentNotValidException.

Your entity won't store incorrect values furthermore it is bad practise to use entity object on the front. You should create a DTO object, than send it to your application. Check it (here you could add incorrect staff to logger) and then process it to your entity object and try to save or whatever you want to do.

Related

resilience4j circuit breaker change fallback method return type than actual called method return type

I am trying to learn Spring Boot microservices. Now I am trying to implement circuit breaker with resilience4j if any of my called service is off.
If I set the fallback method return type as like the actual method return type than it works fine but I can't show the information that my service is off. Because it then send the response null in object's fields. But if I change the return type to String on actual method and also in fallback then I will not be able to get the object value as JSON.
Is it possible to return as string something like Branch service is down!.. with my fallback method and if OK then get the object value as JSON from actual called method? My attempts are below:
My controller method:
#GetMapping("/getById/{id}")
#CircuitBreaker(name = "default", fallbackMethod = "employeeFallback")
public ResponseModelEmployee getEmployee(#PathVariable("id") Long id) {
return employeeService.findByEmployeeId(id);
}
My fallback method in controller:
public ResponseModelEmployee employeeFallback(Long id, Exception ex) {
return new ResponseModelEmployee();
}
My service method called from controller:
public ResponseModelEmployee findByEmployeeId(Long id) {
ResponseModelEmployee empDetails = new ResponseModelEmployee();
...
Branch branch = restTemplate.getForObject("http://BRANCH-SERVICE/branch/getById/" +
employee.get().getBranchId(),
Branch.class);
...
return empDetails;
}
My desire method as fallback:
public String employeeFallback(Long id, Exception ex) {
return "Branch Service is down";
}
If I set my desire method for fallback then it gives the following error:
java.lang.NoSuchMethodException: class com.example.employee.VO.ResponseModelEmployee class com.example.employee.controller.EmployeeController.employeeFallback(class java.lang.Long,class java.lang.Throwable) at io.github.resilience4j.fallback.FallbackMethod.create(FallbackMethod.java:92) ~[resilience4j-spring-1.7.0.jar:1.7.0] ....
Resilince4j expects the fallback method to have the same return type as of the actual method.
Documentation says:
It's important to remember that a fallback method should be placed in
the same class and must have the same method signature with just ONE
extra target exception parameter).
If there are multiple fallbackMethod methods, the method that has the
most closest match will be invoked, for example:
If you try to recover from NumberFormatException, the method with
signature String fallback(String parameter, IllegalArgumentException
exception)} will be invoked.
You can define one global fallback method with an exception parameter
only if multiple methods has the same return type and you want to
define the same fallback method for them once and for all.
So, you cannot directly change the return type to a different one.
You can try few suggestions:
Add #CircuitBreaker and fallback to the service method.
Change return type of service method and fallback method to Object.
One more way could be , you can keep the return type as it is but add a String type message object to response model ResponseModelEmployee. Then set message string to it during fallback.
Another solution could be to return ResponseEntity from the from the method where rest call is made and in the fallback method use ResponseEntity<?> as response object.
you can use Object as a return type
in my case for example:
#GetMapping("/getUser/{id}")
#CircuitBreaker(name= something , fallbackMethod = "ContactsServiceDown")
public ResponseEntity<User> getDetailsById(#PathVariable(id)){
//some logic
return new ResponseEntity<User>(user , HttpStatus.OK);
}
public ResponseEntity<Object> ContactsServiceDown(int id , Exception e){
//some logic
return new ResponseEntity<Object>("ContactsServersDown", HttpStatus.Forbidden)
}
or in returnType ResponseEntity<> leave the type Field empty, hopefully it may work!

Ignoring Jackson InvalidFormatException to use #ExceptionHandler for #Valid #RequestBody

Basically, I'm trying to validate inputs from the #RequestBody of a REST controller using an #ExceptionHandler to catch MethodArgumentNotValidException constraints.
#PostMapping(value = "/createbaseline")
public ResponseEntity<?> createBaseline(#Valid #RequestPart Baseline baseline){
//...
return ResponseEntity.ok("...");
}
Before I can do that Jackson is throwing an InvalidFormatException when it fails to parse a string to a Date, thus preventing my #ExceptionHandler from validating the remaining inputs. Here is my #ExceptionHandler method that I want to use for validating inputs.
#ExceptionHandler(MethodArgumentNotValidException.class)
protected final ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex){
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach(error -> {
String[] mapping = error.getDefaultMessage().split(":");
errors.put(mapping[0], mapping[1]);
});
return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
}
I've considered just changing the Date property of the object to a String, but I know that's against best practices. The reason why I want to use the handleMethodArgumentNotValid is because I have custom constraint annotations on fields in my objects that specify a key that I can access via MethodArgumentNotValidException.getBindingResult() like so:
#RequiredFieldConstraint(key = "label")
private String label;
The client can then use those keys to determine which input field will show an error. If I used the method below to catch the InvalidFormatException then I don't have access to those keys specified by the field annotations.
#ExceptionHandler(value = {InvalidFormatException.class})
protected ResponseEntity<Object> handleException(Exception e){
//can't access annotation keys in this method body
return ResponseEntity.badRequest().body("bad");
}
I need to be able to validate ALL inputs sent in the #ResponseBody in order to send the appropriate error message back to the client but I'd still like to utilize deserializing the #RequestBody directly into an object. Does anyone have ideas on how to approach or work around this?
I found a work around for this by implementing a custom deserializer for Dates so that it catches the InvalidFormatException but still returns a new Date() with .setTime(-1) that I can check for in my handleMethodArgumentNotValid method along with all the other inputs.

Spring MVC error No validator could be found for type

I'm having this error when I try persist a Task class with a join column
This is my join column on Task model
#NotEmpty
#ManyToOne
#JoinColumn(name="project_id")
private Project project;
And this is the controller request.
#RequestMapping(value = "/new", method=RequestMethod.POST, consumes="application/json", headers = "content-type=application/x-www-form-urlencoded")
public String addTask(#Valid Task task, BindingResult result, ModelMap model, HttpServletResponse response) {
//TODO
task.setProject(service.findById(1).getProject());
List<String> errors = service.save(task);
if(errors != null) {
model.addAttribute("errors", errors);
response.setStatus(500);
return "alertErrors";
}
model.addAttribute("tasks", service.findGrid(null));
return "tasks/listTable";
}
But when I call the controller they return me a error No validator could be found for type: ...model.Project..
I'm already put the error on messages and add headers = "content-type=application/x-www-form-urlencoded" to method but still returning error.
What I'm nees to do to fix it?
Per Hibernate Validator documentation you cannot add #NotEmpty on your custom property - it should be used with Strings, Arrays, Collections etc...
You probably should use: javax.validation.constraints.NotNull.

Spring Data Rest ava.lang.IllegalArgumentException

I am getting
java.lang.IllegalArgumentException: Cannot get property 'objects' on null object
error when I intentionally test spring data rest api simulating a user passing bad url as in
http://myurl/findByDate?year=&month="
The year and month are Integers , but in this case I am simulating the user not passing any value.
The app is using the:
public interface myRepo extends PagingAndSortingRepository<myClass, Long> {}
interface and there is no explicit controller provided in a controller class. The data rest interface provides the following:
#Query("Select c from myPOJO c where (YEAR(c.myDate) = :year AND MONTH(c.myDate) = :month)")
Page findByDate(#Param("year") int year,
#Param("month") int month,
Pageable pageable);
I can see why the exception is being thrown ... but providing a generic exception handler to handle it does not seem to resolve the issue.
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> exceptionHandler(Exception ex) {
ErrorResponse error = new ErrorResponse();
error.setErrorCode(HttpStatus.INTERNAL_SERVER_ERROR.value());
error.setMessage("Please contact your administrator");
//return new ResponseEntity<ErrorResponse>(error, HttpStatus.OK);
return new ResponseEntity<ErrorResponse>(error, HttpStatus.METHOD_NOT_ALLOWED);
}
}
Any advise on how to trap this data rest exception would be appreciate it.
java.lang.IllegalArgumentException: Cannot get property 'objects' on null object
Thank you
This is resolved by using the object Integer instead of the primitive Int for the param. This appears to handle the conversion to default value and avoid nulls.
Thanks

Spring #ExceptionHandler, how to preserve model attributes

Seems like #ExceptionHandler clears the model populated by the request handler that threw the exception. Consider the following scenario:
#Controller
public class GreetController {
#RequestMapping(value = "form", method = RequestMethod.GET)
public String showForm(#ModelAttribute("userInfoFormObject") UserInfoForm form) {
return "form";
}
#RequestMapping(value = "processform", method = RequestMethod.POST)
public String processForm(#Valid #ModelAttribute("userInfoFormObject") UserInfoForm form,
BindingResult errors)
throws RegisterFormException {
if (errors.hasErrors())
throw new RegisterFormException();
return "greet";
}
#ExceptionHandler(RegisterFormException.class)
public String registerFormException() {
return "form";
}
}
User inputs invalid data into a register form, RegisterFormException is thrown and exception handler takes user back to register form. Spring jstl tag library expects UserInfoForm object as an model attribute. However, exception handler creates new empty model. Is there way to preserve the populated model across exception handler, or is my only choice to return form view name in the request handler in the case of errors? Is the example solution considered as an anti pattern?

Resources