Ignore field from bindingResult validation - spring-boot

I'm using SpringBoot 2.1.3 (Embedded Tomcat) + Thymeleaf 3 + java 8. I have a problem regarding validation of a UserDTO that is similar to follow:
#Data
public class UserDTO {
#NotNull
private String name;
#NotNull
private String surname;
.....
#NotBlank
#Email
#UniqueEmailConstraint // this is a custom validator
private String email;
#NotNull
private String pass;
.......
}
#UniqueEmailConstraint check inside the DB if the email is present or not (just one email for account are admitted). Than I have 2 controller, one for inserting user and another one for updating user
#PostMapping("/save-user")
String saveUser(#ModelAttribute("userDTO") #Valid UserDto userDto, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "fragment/form-user";
}
and similar one with some other function:
#PostMapping("/update-user")
String updateUser(#ModelAttribute("userDTO") #Valid UserDto userDto, BindingResult bindingResult, Model model) {
if (bindingResult.hasErrors()) {
return "fragment/form-user";
}
The problem is that when I selecting a user to modify it, a thymeleaf view is open and show me all data inserted as expected (mail included). If I try to modify another field, for example Address and click submit my controller show an errors because it find the email on DB.
Question is, is there a way to ignore certain field on bindingResult? Because of I would like to ignore the #UniqueMailConstraint error on second controller Validation.
Thanks all

I had the a similar problem hope this helps:
Lets say there is an employee HTML form with two buttons.
One of the buttons should validate the whole form, another button should validate just one single field and ignore the validation for the rest.
#RequestMapping(method = RequestMethod.POST, value = "saveEmployee", params = "action=save")
public ModelAndView saveEmployee(#Valid #ModelAttribute("employee") EmployeeDTO employee, BindingResult bindingResult, final HttpServletRequest request, final Model model) {
ModelAndView mav = new ModelAndView();
//Create a new BindingResult with zero errors
BindingResult newBindingResult = new BeanPropertyBindingResult(employee, "employee");
//Add to the new BindingResult the error which is caused by the field 'employeeNumber'
newBindingResult.addError(bindingResult.getFieldError("employeeNumber"));
//If required, more fields can be added to the new BindingResult
//Check if the new BindingResult contains errors -> only the fields added above.
if (newBindingResult.hasErrors()) {
//Do this, if the new BindingResult contains validation errors
} else {
//Do that, if the new BindingResult does not contain errors validation errors
//If your form contains other validation errors for fields other than 'employeeNumber', the validation for them will be ignored.
}
//Set the view and return it.
mav.setViewName(xxx);
return mav;
}

Related

Avoid duplicate code in Controllers and service

I am using Spring framework.
I wrote some pretty long code to save some results.
So later it turned out in other controllers I will also need this code. Just with small differences for example returning some other strings.
So of course every controller will have its own mapping. So the parameters will be duplicate anyway.
But now for the code inside the mapping method.
I was thinking putting the code in the service of the original controller. Then the other controllers will call this service. The service of course will have plenty of parameters + the slight differences between the controllers. Or should I make like a general service, have good documentation there because of course the methods there will be general and later I should know what they were for.
#PostMapping("/testcase") public RedirectView saveResult(Model model, #ModelAttribute("testResultEntity") TestResultEntity testResultEntity, RedirectAttributes redirectAttributes , #RequestParam(required = false) String version , #RequestParam(required = false,defaultValue = "0") String page, #RequestParam(required = false) String operation, Authentication authentication,Locale locale)
{ // here comes long code, which will be used also in other controllers ;
}
If all the controller mappings have the same signature you can create a parent class with the common implementation.
Something like this:
public abstract class BaseAbstractController {
// specific logic per controller
abstract String specific();
public RedirectView save(Model model, #ModelAttribute("testResultEntity") TestResultEntity testResultEntity,
RedirectAttributes redirectAttributes, #RequestParam(required = false) String version,
#RequestParam(required = false, defaultValue = "0") String page, #RequestParam(required = false) String operation,
Authentication authentication, Locale locale) {
// here comes long code, which will be used also in other controllers ;
String specific = specific();
}
}
#Controller
public class TestController extends BaseAbstractController {
#Override
String specific() {
return "something"; // here goes your specific logic;
}
#PostMapping("/testcase")
public RedirectView saveResult(Model model, #ModelAttribute("testResultEntity") TestResultEntity testResultEntity,
RedirectAttributes redirectAttributes, #RequestParam(required = false) String version,
#RequestParam(required = false, defaultValue = "0") String page, #RequestParam(required = false) String operation,
Authentication authentication, Locale locale) {
return save(model, testResultEntity, redirectAttributes, version, page, operation, authentication, locale);
}
}

Stop some validations at the persist time in Spring

Is there a way to stop some validations to be executed at the time of data persist.
I know about
spring.jpa.properties.javax.persistence.validation.mode=none
but I believe that disables all validations, I just want to disable it for some of the fields (Password specifically, as it is encoded by that time and then pattern doesn't match). Thanks!
Update(More Details):
#Pattern(regexp = "^[A-Za-z0-9_#!]+$")
private String password;
I have a password field validated using the above pattern (A-Z, a-z, 0-9 and _,#,!). In the controller it validates success by below code.
#RequestMapping(value = "/adduser", method = RequestMethod.POST)
public ModelAndView signUp(ModelAndView modelAndView, #ModelAttribute #Valid LoginUser loginUser, BindingResult bindingResult) {
LoginUser loginUserAdded = null;
if (!bindingResult.hasErrors()) {
loginUser.setPassword(passwordEncoder.encode(loginUser.getPassword()));
loginUserAdded = createUser(loginUser);
....
But then before persist I encode the password and then it throws error while calling save method in JpaRepository because the password value has been changed by the encoder and it doesnt satisfy the pattern validation applied to it.
Now I am looking for a way by which I can disable validation on this field at the time of persist.

Spring MVC forward appending request parameter values comma separated when we have same parameter name for topRequest and forward request

Spring MVC forward appending request parameter values comma separated when we have same parameter name for topRequest and forward request
#RequestMapping(path = "/details")
public ModelAndView details(#ModelAttribute final DetailsForm detailsForm){
//DetailsForm contain a parameter called destinationId with value 1234
final ModelAndView mav = new ModelAndView();
//Some logic to get targeted destinationId (7890) using destinationId (1234) from detailForm
mav.setViewName("forward:/search?destinationId=7890");
return mav;
}
#RequestMapping(path = "/search")
public ModelAndView details(#ModelAttribute final SearchForm searchForm){
//Here I tried to get destinationId from model-attribute searchForm
final Integer destinationId = searchForm.getDestinationId();
//Then it returned me 1234,7890
}
Can someone please help me out how to resolve this. I want to get only 7890.
I am interested in the answer also. I also hit this problem hacked it by adding a method:
private String getLastPartFromFormValue(final String value) {
if (value == null)
return null;
String[] parts = value.split(",");
return parts[parts.length -1];
}
Just for sake of knowledge.
If you have a method, and you have a query param named thing and have an object annotated with #ModelAttribute and, in that object you have a field with the same name of your query param, you can expect that behavior.
For example:
#PostMapping(value = "/my-awesome-path")
public String myAwesomeMethod(
#RequestParam(name = "token", required = false) final String token,
#ModelAttribute("formData") final MyFormData formData) {
//class fields and members...
And, in the MyFormData you have this:
public class MyFormData{
private String token;
//other fields, getters and setters...
You will receive the query param token with the value duplicated and comma separated as well as in the MyFormData object.
The alternative is check the casuistic and change the parameter name or redesign if it's necessary.
In the example the query param name is changed like this:
#PostMapping(value = "/my-awesome-path")
public String myAwesomeMethod(
#RequestParam(name = "custom-token", required = false) final String customToken,
#ModelAttribute("formData") final MyFormData formData) {
//class fields and members...
And the values are not more duplicated nor comma separated.
Cheers.

Update one attribute of an Entity with ModelAttribute

How can I update just one or a few atributes of an Entity with spring form and controller?
Lets say it is User Entity and has id, status, name, address etc...
I want to update just name, and address. But when I try to save ather values is null. ı dont want to show all attributes in form logically ( Id, status )
You can use hidden input element to propagate users ID to your view, e.g.
<input type="hidden" name="user-id" value="${editUserForm.id}">
Put it in a form - when a form is submitted, users ID will also be submitted with it (remember to add ID to your form model). Then retrieve user from database using this ID, set fields you want to set and update it.
EDIT:
Example:
your model:
#Entity
public class User{
private Long id;
private String name;
private String surname;
//getters & setters
}
form you use to edit some of the fields (no surname):
public class UserForm{
private Long id;
private String name;
//getters & setters, constructor
}
Controller:
#GetMapping(value="/editUser/{userId}")
public ModelAndView editUser(#PathVariable Long userId){
ModelAndView modelAndView = new ModelAndView("editUser");
User user = // retrieve user from database using userId
modelAndView.addObject("editUserForm", new UserForm(user));
return modelAndView;
}
#PostMapping(value="/editUser")
public ModelAndView postEditUser(#ModelAttribute("editUserForm") UserForm editUserForm){
User userToEdit = //retrive user from database using editUserForm.getId()
userToEdit.setName(editUserForm.getName());
//save user to database
//redirect
}
Of course logic I presented in controllers should be located in service layer, I just want to give you an idea on what to do.

400 Bad request with Hibernate #Valid

I have a strange behaviour when I validate my form.
As soon as I add the Hibernate #Valid annotation, Tomcat consided my request as "bad" if the posted data are not valid. If the data are valid, no worries.
I use:
Tomcat 7.0.52
Javax Validation api 1.1.0.Final
Hibernate Validator 5.1.0.Final
Spring 4.0.3.RELEASE
At the moment, I do a really simple validation:
public class RemoveCacheElementForm {
#NotBlank(message = "Please select a cache name.")
private String name;
#NotBlank(message = "Please select a cache entry key.")
private String key;
The Spring controller:
/**
* Handler to remove one cached elements from the specified cache.
*
* #return the view.
*/
#RequestMapping(value = CACHE_REMOVE, method = RequestMethod.POST)
public String removeCachedElement(ModelMap model, #Valid #ModelAttribute(FORM_NAME) RemoveCacheElementForm form) {
model.addAttribute("removeElementResult", CacheUtils.removeCachedElement(form.getName(), form.getKey()));
initializeModel(model);
return CACHE_ADMIN_PAGE;
}
When I remove #Valid annotation, no worries too.
Anyone has an idea?
Thanks a lot for your help! :-)
Try changing your code to
#RequestMapping(value = CACHE_REMOVE, method = RequestMethod.POST)
public String removeCachedElement(ModelMap model, #Valid #ModelAttribute(FORM_NAME) RemoveCacheElementForm form, BindingResult bindingResult) {
model.addAttribute("removeElementResult", CacheUtils.removeCachedElement(form.getName(), form.getKey()));
initializeModel(model);
return CACHE_ADMIN_PAGE;
}

Resources