I am using JSR 303 for validating my request object of rest controller. Validation is working fine but i am not getting the response. below is the code am i missing something ?
Pojo class:
#ValInfo(message ="empty filed", groups=ExtendedValidation.class)
public class Request {
Validator classes:
#Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = { ValInfoValidator.class })
#Documented
public #interface ValInfo {
String message() default "{Client information invalid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class ValInfoValidator implements ConstraintValidator<ValInfo, TvxOnlineRequest> {
#Override
public void initialize(ValInfo arg0) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(TvxOnlineRequest arg0, ConstraintValidatorContext arg1) {
// TODO Auto-generated method stub
return false;
}
Related
I'm using Spring Boot 2.4. I have the following controller with a method that accepts a MultipartFile object.
#RestController
public class MyController extends AbstractController
...
#Override
public ResponseEntity<ResponseData> add(
...
#Parameter(description = "file detail") #Validated #RequestPart("myFile")
MultipartFile myFile,
...
) {
I would like to validate that this MultipartFile contains the data that I want (e.g. is of a particular mime type). So I have written the below validator ...
#Documented
#Constraint(validatedBy = MultipartFileValidator.class)
#Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface MultipartFileConstraint {
String message() default "Incorrect file type.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and its implementation class ...
public class MultipartFileValidator
implements ConstraintValidator<MultipartFileConstraint, MultipartFile> {
#Override
public void initialize(final MultipartFileConstraint constraintAnnotation) {
log.info("\n\n\n\nconstructor called\n\n\n\n");
}
#Override
public boolean isValid(
MultipartFile file, ConstraintValidatorContext constraintValidatorContext) {
log.info("Validating file");
...
}
}
However, when I invoke my endpoint, I don't see that my validator is called (for one, the log statement is never printed nor breakpoints hit). What else do I need to do to register my validator for this MultipartFile param?
As per the Spring Documentation:
Can also be used with method level validation, indicating that a
specific class is supposed to be validated at the method level (acting
as a pointcut for the corresponding validation interceptor), but also
optionally specifying the validation groups for method-level
validation in the annotated class. Applying this annotation at the
method level allows for overriding the validation groups for a
specific method but does not serve as a pointcut; a class-level
annotation is nevertheless necessary to trigger method validation for
a specific bean to begin with. Can also be used as a meta-annotation
on a custom stereotype annotation or a custom group-specific validated
annotation.
So, here we have to keep in mind what are the placement of #Validated and validator annotation.
Code:
Controller class : #Validated added at class level and #ValidFile (Custom validator annotation) in the method
#RestController
#Validated
#Slf4j
public class MyController {
#RequestMapping("/add")
public ResponseEntity<ResponseData> add(#ValidFile #RequestParam("file") MultipartFile file) {
log.info("File Validated");
return ResponseEntity.status(HttpStatus.OK).body(new ResponseData("Valid file received"));
}
}
Validator Annotation
#Documented
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {FileValidator.class})
public #interface ValidFile {
Class<? extends Payload> [] payload() default{};
Class<?>[] groups() default {};
String message() default "Only pdf,xml,jpeg,jpg files are allowed";
}
Validator class
#Slf4j
public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {
#Override
public void initialize(ValidFile validFile) {
log.info("File validator initialized!!");
}
#Override
public boolean isValid(MultipartFile multipartFile,
ConstraintValidatorContext constraintValidatorContext) {
log.info("Validating file");
String contentType = multipartFile.getContentType();
assert contentType != null;
return isSupportedContentType(contentType);
}
private boolean isSupportedContentType(String contentType) {
return contentType.equals("application/pdf")
|| contentType.equals("text/xml")
|| contentType.equals("image/jpg")
|| contentType.equals("image/jpeg");
}
}
Output :
Success:
{
"message": "Valid file received"
}
Exception handler
#ExceptionHandler(ConstraintViolationException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>("Validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
Failure:
Validation error: Only pdf,xml,jpeg,jpg files are allowed
Below is a small example. I hope it will help.
#Component
public class MultipartFileValidator implements Validator {
#Override
public boolean supports(Class < ? > clazz) {
return MultipartFile.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MultipartFile multipartFile = (MultipartFile) target;
if (multipartFile.isEmpty()) {
// Add an error message to the errors list
errors.rejectValue("file", "required.file");
}
}
}
I've created a custom constraint validator that works on a list of objects. Unfortunately it doesn't seem to be getting invoked, it worked when I had a wrapper class containing the list with the annotation on the list.
This is the code that worked fine
public class wrapper {
#ValidMyObjectList
List<MyObject> myObjects;
...
}
But now I've got rid of the wrapper class and added the annotation to the parameter in the controller method.
Here's the controller
#RequestMapping(value = "", method = RequestMethod.POST)
public List<MyObject> stopCheque(
#ValidMyObjectList #RequestBody final List<MyObject> myObjects,
final HttpServletResponse httpServletResponse) {
....
}
Here's the constraint annotation
#Constraint(validatedBy = MyObjectListValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER })
public #interface MyObjectList {
Class<?>[] groups() default {};
String message() default "";
Class<? extends Payload>[] payload() default {};
}
And part of the validator itself
public class MyObjectListValidator implements
ConstraintValidator<MyObjectList, List<MyObject>> {
#Override
public void initialize(final MyObjectList myObjectList) {
}
#Override
public boolean isValid(final List<MyObjectList> myObjectLists, final ConstraintValidatorContext cxt) {
...
}
Would greatly appreciate any help. Thanks
Add to your Spring config class:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
and add #Validated to controller class.
I have controller with method:
void save(#Validated(BookingForm.ValidationSave.class) #RequestBody BookingForm form, Errors result) {...}
and BookingForm with a field and my custom validator:
public class BookingForm {
public interface ValidationSave {}
public interface ValidationEstimate {}
....
#Future
private LocalDateTime when;
....
}
Future.java
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = FutureValidator.class)
#Documented
public #interface Future {
String message() default "{javax.validation.constraints.Future.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
I've expected that validator will valid my 'when' field but it does not.
When I removed the group from #Validated it works. Any ideas?
Works fine with #NotNull and others javax validators.
Updated:
FutureValidator.java
public class FutureValidator implements ConstraintValidator<Future, Temporal> {
#Override
public void initialize(Future constraintAnnotation) {
}
#Override
public boolean isValid(Temporal value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
LocalDate ld = LocalDate.from(value);
return ld.isAfter(LocalDate.now());
}
}
I am trying to implement custom annotation in spring as follows:-
Test annotation is as follows
#Documented
#Target( { ElementType.METHOD, ElementType.FIELD })
#Constraint(validatedBy=CheckUser.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface Test {
String user();
}
And for validation I have written CheckUser class as follows::-
private String xyz;
#Override
public void initialize(Test user) {
xyz=user.toString();
}
#Override
public boolean isValid(Principal principal, ConstraintValidatorContext context) {
if(principal.getName().equalsIgnoreCase(xyz))
return true;
else
return false;
}
But its not working. Can some one tell me what is wrong here??
In security as well as application context I have written
<Secured:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" jsr250-annotations="enabled"/>
You need to include message, groups, and payload so it plays nicely with regards to the JSR-303 spec: http://beanvalidation.org/1.0/spec/#constraintsdefinitionimplementation-constraintdefinition-properties
#Documented
#Target( { ElementType.METHOD, ElementType.FIELD })
#Constraint(validatedBy=CheckUser.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface Test {
String user();
String message() default "test failed"
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Using Jersy 2 on Glassfish 4.
I have a custom ConstraintValidator that is being called to validate a bean parameter.
The ConstraintValidator is correctly injected with Jersey resources using #Context. (Changed #Context to #Inject to solve problem with resources being null when ConstraintValidator is called twice by the system.)
Problem the isValid() method is called twice I can see the logging being printed twice before the update() method is being called.
I added an interceptor to do some debug logging.
First the constraintvalidator.isValid() is called then my Interceptor then constraintvalidator.isValid() is called again and then only my REST resource method.
(This class does not contain any injected resources.)
public class StudyValidator implements ConstraintValidator<StudyCheck, StudyBeanREST> {
private static final Logger log = Logger.getLogger(StudyValidator.class);
#Override
public void initialize(StudyCheck constraintAnnotation) {
}
#Override
public boolean isValid(StudyBeanREST study, ConstraintValidatorContext context) {
log.info("Validating study: " + study);
Integer version = study.getVersion();
if(version == null || version < 0) {
return false;
}
return true;
}
}
The annotation:
#Target({ElementType.PARAMETER,ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = {StudyValidator.class})
public #interface StudyCheck {
String message() default "{error.version}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And where I use my annotation:
#PUT
#RolesAllowed({"management"})
public StudyBeanREST update(#StudyCheck StudyBeanREST study) throws RecordNotFoundException, UpdateNotAllowedException {
Study updated = studyEJB.update(study.getJpa());
study.setJpa(updated);
return study;
}