Spring invalid target for Validator - java

I am trying to validate the string email to check if it already appears within my MYSQL database, when I execute with an email thats already used I get the following error:
java.lang.IllegalStateException: Invalid target for Validator [co2103.hw2.controller.TestResultsValidator#62b41c6]: org.springframework.validation.BeanPropertyBindingResult: 1 errors
Field error in object 'testResults' on field 'email': rejected value [abc#le.ac.uk]; codes [email.testResults.email,email.email,email.java.lang.String,email]; arguments []; default message [is already provided by a different user! Please user another one!]
Here is the validator code
public class TestResultsValidator implements Validator{
private TestResultsRepository TrRepo;
private HomeTestRepository HTRepo;
public TestResultsValidator (TestResultsRepository TrRepo, HomeTestRepository HTRepo) {
this.TrRepo = TrRepo;
this.HTRepo = HTRepo;
}
#Override
public boolean supports(Class<?> clazz) {
return TestResults.class.equals(clazz);
}
#Override
public void validate(Object target, Errors errors) {
TestResults tr = (TestResults) target;
for(TestResults t : TrRepo.findAll()) {
//SAME EMAIL
if (tr.getEmail().equals(t.getEmail())) {
errors.rejectValue("email", "email", "is already provided by a different user! Please user another one!");
System.out.println("Email is already taken by a different user, please try another username");
break;
}
The controller code
//Add new results
#RequestMapping(value = "/addResults",method = {RequestMethod.POST , RequestMethod.GET})
public String newHotel(#Valid #ModelAttribute TestResults results, BindingResult result, Model model) {
if (result.hasErrors()) {
model.addAttribute("errors", result);
return "start";
}
else {
trRepo.save(results);
return "Submitted";
}}

You need to register you validator to spring.
First ad Component Annotation to your validtor.
#Component
public class TestResultsValidator implements Validator{
.....
}
Register it in the controller.
#Controller
class TestResultController {
#Autowired
TestResultsValidator testResultsValidator ;
#InitBinder("testResultsValidator")
protected void initMessageBinder(WebDataBinder binder) {
binder.addValidators(testResultsValidator );
}
}

Related

How to write custom validation in rest api?

In Spring boot.
I want to do field validation and return an error if the input does not exist in the database.
I am trying to write the custom annotation for multiple input fields.
The controller is as below
#RestController
#Api(description = "The Mailer controller which provides send email functionality")
#Validated
public class SendMailController {
#Autowired
public SendMailService sendemailService;
org.slf4j.Logger logger = LoggerFactory.getLogger(SendMailService.class);
#RequestMapping(method = RequestMethod.POST, value = "/sendMail", consumes = {MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, produces = {"text/xml", "application/json"})
#ResponseBody
#Async(value = "threadPoolTaskExecutor")
#ApiOperation("The main service operation which sends one mail to one or may recipient as per the configurations in the request body")
public Future<SendMailResult> sendMail(#ApiParam("Contains the mail content and configurations to be used for sending mail") #Valid #RequestBody MailMessage message) throws InterruptedException {
SendMailResult results = new SendMailResult();
try {
sendemailService.sendMessages(message);
long txnid = sendemailService.createAudit (message);
results.setTxnid (txnid);
results.setStatus("SUCCESS");
} catch(MessagingException | EmailServiceException e) {
logger.error("Exception while processing sendMail " + e);
results.setStatus("FAILED");
// TODO Handle error create results
e.printStackTrace();
} catch(Exception e) {
logger.error("Something went wrong " + e);
results.setStatus("FAILED");
// TODO Handle error create results
e.printStackTrace();
}
return new AsyncResult<SendMailResult>(results);
}
}
one DTO that is mapped with request
public class MailContext {
#NotNull
private String clientId;
#NotNull
private String consumer;
public int getClientId() {
return Integer.parseInt(clientId);
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String toJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
String writeValueAsString = mapper.writeValueAsString(this);
return writeValueAsString;
}
}
Request xml
<mailMessage>
<mailContext>
<clientId>10018</clientId>
<consumer>1</consumer>
</mailContext>
</mailMessage>
I want to write a custom annotation to validate client which exists in the database (table client_tbl) if provided in the request.
consumer: is present in database table cunsumer_tbl
if these not present in database send error message else call service method.
Please suggest how to write such custom annotation with the error.
I know another way to validate this.
Inside your controller, you can register a validator.
#InitBinder
public void setup(WebDataBinder webDataBinder) {
webDataBinder.addValidators(dtoValidator);
}
Where dtoValidator is an instance of Spring Bean, for example, which must implements org.springframework.validation.Validator.
So, you just have to implement two methods: supports() and validate(Object target, Errors errors);
Inside supports() method you can do whatever you want to decide whether the object should be validated by this validator or not. (for example, you can create an interface WithClientIdDto and if the tested object isAssignableFrom() this interface you can do this validation. Or you can check your custom annotation is presented on any field using reflection)
For example: (AuthDtoValidator.class)
#Override
public boolean supports(Class<?> clazz) {
return AuthDto.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
final AuthDto dto = (AuthDto) target;
final String phone = dto.getPhone();
if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(dto.getEmail())) {
errors.rejectValue("email", "", "The phone or the email should be defined!");
errors.rejectValue("phone", "", "The phone or the email should be defined!");
}
if (!StringUtils.isEmpty(phone)) {
validatePhone(errors, phone);
}
}
UPDATE:
You can do that.
Create an annotation
for example:
#Target({ FIELD })
#Retention(RUNTIME)
#Constraint(validatedBy = ClientIdValidator.class)
#Documented
public #interface ClientId {
String message() default "{some msg}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
and implement this validator:
class ClientIdValidator implements ConstraintValidator<ClientId, Long> {
#Override
public boolean isValid(Long value, ConstraintValidatorContext context) {
//validation logc
}
}
More details you can find here: https://reflectoring.io/bean-validation-with-spring-boot/

Java Validating a extended Pojo

I am building project on spring boot and want to add validation that are easy to integrate.
I have Pojo for my project as below:
public class Employee{
#JsonProperty("employeeInfo")
private EmployeeInfo employeeInfo;
}
EmployeeInfo class is as below:
public class EmployeeInfo extends Info {
#JsonProperty("empName")
private String employeeName;
}
Info class is as below:
#JsonIgnoreProperties(ignoreUnknown = true)
public class Info {
#JsonProperty("requestId")
protected String requestId;
}
How to I validate if request Id is not blank with javax.validation
My controller class is as below:
#RequestMapping(value = "/employee/getinfo", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<> getEmployee(#RequestBody Employee employee) {
//need to validate input request here
//for e.g to check if requestId is not blank
}
Request :
{
"employeeInfo": {
"requestId": "",
}
}
Considering you are making use of validation-api:
Please try using below to validate if your String is not null or not containing any whitespace
#NotBlank
In order to validate request parameters in controller methods, you can either use builtin validators or custom one(where you can add any type of validations with custom messages.)
Details on how to use custom validations in spring controller, Check how to validate request parameters with validator like given below:
#Component
public class YourValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return clazz.isAssignableFrom(Employee.class);
}
#Override
public void validate(Object target, Errors errors) {
if (target instanceof Employee) {
Employee req = (Employee) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "employeeInfo.requestId", "YourCustomErrorCode", "yourCustomErrorMessage");
//Or above validation can also be done as
if(req.getEmployeeInfo().getRequestId == null){
errors.rejectValue("employeeInfo.requestId", "YourCustomErrorCode", "YourCustomErrorMessage");
}
}
}
}

Using Spring MVC Validator in controller using multiple domain objects

I have a Spring MVC controller that handles requests to do with user management including a method to save user details and a method to allow a user to reset their password. I want to use a validator to ensure that the user typed the same password twice.
My controller
#Controller
public class UserDetails {
...
#InitBinder
public void binder(WebDataBinder binder) {
binder.addValidators(new PasswordValidator());
}
...
#RequestMapping(value="/saveUserDetails", method=RequestMethod.POST)
public String saveUserDetails(
#ModelAttribute User user) {
...
}
...
#RequestMapping(value="/resetPassword", method=RequestMethod.POST)
public String resetPassword(
#Validated PasswordPair password, BindingResult result) {
...
}
And the Validator
private final static class PasswordValidator implements Validator {
private final static int MIN_LEN=5;
#Override
public boolean supports(Class<?> clazz) {
return PasswordPair.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
PasswordPair pair = (PasswordPair)target;
//1 impose password rules
if (pair.getPassword().length()<MIN_LEN) {
errors.rejectValue("password", "Too short", "Password must bne at least "+MIN_LEN+" chars");
}
if (!pair.getPassword().equals(pair.getConfirmPassword())) {
errors.rejectValue("confirmPassword", "mustMatch", "passwords must match");
}
}
}
The problem is that when saveUserDetails is called spring is trying to validate the user with the password validator.
I have tried changing #InitBinder to #InitBinder("password") or #InitBinder("PasswordPair") but in that case nothing is validated at all.
How can I make it validate only the correct parameters?
Your approach of using #InitBinder(withname) should work , my guess is that you have just provided the wrong name.
Can you try this name:
#InitBinder("passwordPair")
Another option will to explicitly give your ModelAttribute a name, this way and use that name in the InitBinder:
public String resetPassword(
#ModelAttribute("password") #Valid PasswordPair password, BindingResult result) {
...
}
#InitBinder("password")
On a related note, I have a similar issue recorded with Spring Jira and have a pull request to change this behavior in place. Please vote it up if possible - https://jira.springsource.org/browse/SPR-11429
Add password and confirmPassword field to the User object instead of having PasswordPair object. Change resetPassword method to have user as inparameter.
And change the validator to validate the user. Here i suppose that you would have password validation even when create a new user?
Ex:
#Controller
public class UserDetails {
#InitBinder
public void binder(WebDataBinder binder) {
binder.addValidators(new PasswordValidator());
}
#RequestMapping(value="/saveUserDetails", method=RequestMethod.POST)
public String saveUserDetails(#ModelAttribute User user) {
...
}
#RequestMapping(value="/resetPassword", method=RequestMethod.POST)
public String resetPassword(#Valid #ModelAttribute User user, BindingResult result) {
...
}
And the Validator
private final static class UserValidator implements Validator {
private final static int MIN_LEN=5;
#Override
public boolean supports(Class<?> clazz) {
return PasswordPair.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
User user = (User)target;
//1 impose password rules
if (user.getPassword().length()<MIN_LEN) {
errors.rejectValue("password", "Too short", "Password must bne at least "+MIN_LEN+" chars");
}
if (!user.getPassword().equals(user.getConfirmPassword())) {
errors.rejectValue("confirmPassword", "mustMatch", "passwords must match");
}
}
}
or you could look at this post, maybe it'll help you:
Handling password confirmations on spring-mvc

Removing duplication from Spring controllers

I have been looking for a way to somehow reduce the amount of code that is duplicated with subtle variance in my Spring MVC controllers, but searching through the SO questions so far has only yielded some questions without any satisfactory answers.
One example of duplication that I want to remove is this, where the user creation page and the role creation page share similarities:
#RequestMapping(value = "user/create", method = RequestMethod.GET)
public String create(#ModelAttribute("user") User user, BindingResult errors) {
LOG.debug("Displaying user creation page.");
return "user/create";
}
#RequestMapping(value = "role/create", method = RequestMethod.GET)
public String create(#ModelAttribute("role") Role role, BindingResult errors) {
LOG.debug("Displaying role creation page.");
return "role/create";
}
A slightly more involved variant of duplication that I would like to remove is the one for posting the create form:
#RequestMapping(value = "user/create", method = RequestMethod.POST)
public String save(#ModelAttribute("user") User user, BindingResult errors) {
LOG.debug("Entering save ({})", user);
validator.validate(user, errors);
validator.validatePassword(user, errors);
validator.validateUsernameAvailable(user, errors);
String encodedPassword = encoder.encode(user.getPassword());
user.setPassword(encodedPassword);
if (errors.hasErrors()) {
return create(user, errors);
} else {
service.save(user);
}
return "redirect:/user/index/1";
}
#RequestMapping(value = "role/create", method = RequestMethod.POST)
public String save(#ModelAttribute("role") Role role, BindingResult errors) {
LOG.debug("Entering save({})", role);
validator.validate(role, errors);
if (errors.hasErrors()) {
return create(role, errors);
} else {
service.save(role);
}
return "redirect:/index";
}
This example includes a validate then save if correct and a redirect to the error page if things don't go as planned.
How to remove this duplication?
Spring uses your handler method parameter types to create class instances from the request parameters or body. As such, there is no way to create a handler (#RequestMapping) method that could take an Object and check if it is either a Role or a User. (Technically you could have both parameters and just check which one isn't null, but that is terrible design).
Consequently, you need a handler method for each. This makes sense since, even through the logic is similar, it is still specific to the exact type of model object you are trying to create. You perform different validation, call a different service method, and return a different view name.
I say your code is fine.
Thought I would provide the solution that I settled on in the hope that it might help someone. My gf suggested that I use the name of the entity as a path variable for the controller, and this has proved to provide a very nice solution for the problem at hand.
The two methods now look like this:
#RequestMapping(value = "{entityName}/create", method = RequestMethod.GET)
public String create(#PathVariable("entityName") String entityName, #ModelAttribute("entity") BaseEntity entity, BindingResult errors) {
LOG.debug("Displaying create page for entity named: [{}]", entityName);
return handlerFactory.getHandler(entityName).getCreateView();
}
#RequestMapping(value = "{entityName}/create", method = RequestMethod.POST)
public String save(#PathVariable("entityName") String entityName, #ModelAttribute("entity") BaseEntity entity, BindingResult errors) {
LOG.debug("Saving entity of type {}", entityName);
CrudHandler handler = handlerFactory.getHandler(entityName);
handler.getCreateValidator().validate(entity, errors);
if (errors.hasErrors()) {
return create(entityName, entity, errors);
}
handler.preSave(entity);
handler.getService().save(entity);
return "redirect:" + DASHBOARD_URL;
}
The CrudHandler interface has implementations for each entity, and provides the controller with the entity specific classes that it needs, such as service and validator. A sample CrudHandler implementation looks like this for me:
#Component
public class RoleCrudHandler implements CrudHandler {
private static final String ENTITY_NAME = "role";
public static final String CREATE_VIEW = "role/create";
public static final String EDIT_VIEW = "role/edit";
#Resource
private RoleService roleService;
#Resource
private RoleValidator validator;
#Resource
private CrudHandlerFactory handlerFactory;
#PostConstruct
public void init() {
handlerFactory.register(ENTITY_NAME, this);
}
#Override
public GenericService getService() {
return roleService;
}
#Override
public Validator getCreateValidator() {
return validator;
}
#Override
public Validator getUpdateValidator() {
return validator;
}
#Override
public BaseEntity createEntity() {
return new Role();
}
#Override
public void preSave(BaseEntity entity) {
}
#Override
public String getCreateView() {
return CREATE_VIEW;
}
#Override
public String getUpdateView() {
return EDIT_VIEW;
}
}
If someone sees some ways to improve this, feel free to share. Hope this will be of use for someone.

Spring - disable bind exceptions (for a particular property)

In a web application I'm working on using Spring 2.5.6.SEC01, I essentially have an Integer field that takes a number to determine which page to scroll to. The requirements changed, and we no longer want to display an error message, but simply ignore the user's input if they enter an invalid number, say "adfadf".
I was reading that you can do that via:
TypeMismatch.property=Some New Error Message
However, after having tried that, we are still getting the original error message:
java.lang.Integer.TypeMismatch=...
I only want to disable this message for that given property. How can I do that? I still want binding to occur automatically, I just don't want to hear about it now.
Walter
According to DefaultMessageCodesResolver
In case of code "typeMismatch", object name "user", field "age"
typeMismatch.user.age
typeMismatch.age
typeMismatch.int
typeMismatch
So you should get (I suppose your commandName is called command and your property is age) Adapt according to your code
typeMismatch.command.age
typeMismatch.age
typeMismatch.java.lang.Integer
typeMismatch
Notice The third code
typeMismatch.java.lang.Integer
It will solve what you want
UPDATE
I have created a Person command class
public class Person implements Serializable {
private Integer age;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
And a person controller
public class PersonController extends SimpleFormController {
public PersonController() {
setCommandClass(Person.class);
setValidator(new Validator() {
public boolean supports(Class clazz) {
return clazz.isAssignableFrom(Person.class);
}
public void validate(Object command, Errors errors) {
rejectIfEmpty(errors, "age", "Age is required");
}
});
}
#Override
protected ModelAndView onSubmit(Object command) throws Exception {
return new ModelAndView();
}
}
Here goes my myMessages.properties (root of the classpath)
typeMismatch.command.age=typeMismatch.command.age
typeMismatch.age=typeMismatch.age
typeMismatch.java.lang.Integer=typeMismatch.java.lang.Integer
typeMismatch=typeMismatch
So, i have done the following test
public class PersonControllerTest {
private PersonController personController;
private MockHttpServletRequest request;
private MessageSource messageSource;
#Before
public void setUp() {
request = new MockHttpServletRequest();
request.setMethod("POST");
personController = new PersonController();
messageSource = new ResourceBundleMessageSource();
((ResourceBundleMessageSource) messageSource).setBasename("myMessages");
}
#Test
public void failureSubmission() throws Exception {
/**
* Ops... a bindException
*
* Age can not be a plain String, It must be a plain Integer
*/
request.addParameter("age", "not a meaningful age");
ModelAndView mav = personController.handleRequest(request, new MockHttpServletResponse());
BindingResult bindException = (BindingResult) mav.getModel().get(BindingResult.MODEL_KEY_PREFIX + "command");
for (Object object : bindException.getAllErrors()) {
if(object instanceof FieldError) {
FieldError fieldError = (FieldError) object;
assertEquals(fieldError.getField(), "age");
/**
* outputs typeMismatch.command.age
*/
System.out.println(messageSource.getMessage((FieldError) object, null));
}
}
}
}
If you want the second one, you must get rid of typeMismatch.command.age key resource bundle
typeMismatch.age=typeMismatch.age
typeMismatch.java.lang.Integer=typeMismatch.java.lang.Integer
typeMismatch=typeMismatch
Or write your own implementation of MessageCodesResolver
public class MyCustomMessageCodesResolver implements MessageCodesResolver {
private DefaultMessageCodesResolver defaultMessageCodesResolver = new DefaultMessageCodesResolver();
public String [] resolveMessageCodes(String errorCode, String objectName) {
if(errorCode.equals("age"))
/**
* Set up your custom message right here
*/
return new String[] {"typeMismatch.age"};
return defaultMessageCodesResolver.resolveMessageCodes(String errorCode, String objectName);
}
public void String[] resolveMessageCodes(String errorCode, String objectName, String field, Class fieldType) {
if(errorCode.equals("age"))
/**
* Set up your custom message right here
*/
return new String[] {"typeMismatch.age"};
return defaultMessageCodesResolver.resolveMessageCodes(String errorCode, String objectName, String field, Class fieldType);
}
}
And set up your PersonController
public class PersonController extends SimpleFormController {
public PersonController() {
setMessageCodesResolver(new MyCustomMessageCodesResolver());
setCommandClass(Person.class);
setValidator(new Validator() {
public boolean supports(Class clazz) {
return clazz.isAssignableFrom(Person.class);
}
public void validate(Object command, Errors errors) {
rejectIfEmpty(errors, "age", "Age is required");
}
});
}
You can register a custom PropertyEditor for that field, which wouldn't fail on type mismatch.
Since this is a Spring MVC application and assuming that it is a simple form, you can set this up in many ways. Can you specify your controller settings? For post request, you can record a suppressed field before the validator is called (assuming you have specified one) or after the validator is called. If you want to do it before validation, you can call [this][2]. After validation, you can call [this][3]
[2]: http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/web/servlet/mvc/BaseCommandController.html#onBind(javax.servlet.http.HttpServletRequest, java.lang.Object, org.springframework.validation.BindException)
[3]: http://static.springsource.org/spring/docs/2.0.x/api/org/springframework/web/servlet/mvc/BaseCommandController.html#onBindAndValidate(javax.servlet.http.HttpServletRequest, java.lang.Object, org.springframework.validation.BindException)

Categories

Resources