constraint validation on #RequestParam does not seems to be working - spring

I have a GetAll operation that pull all employees in a deptNo. I want to add a constraint that checks the size on deptNumber on my RequestParameter but it doesn't seems to be validating the parameter.
How can I achieve the validation?
public ResponseEntity<PagedResources<EmpSummary>> getEmps(
#Valid #RequestParam #Max(1) #Max(999) Optional<Long> deptNUmber)
#Controller
#Validated
#RequestMapping("/v1")
#RequiredArgsConstructor
public class OntApiController implements OntApi {
private final OntService service;
private NativeWebRequest nativeWebRequest;
private HttpServletRequest httpServletRequest;
#Override
public ResponseEntity<PagedResources<OntSummaryResource>> getOnts(
#Valid #RequestParam #Min(1L) #Max(999L) Optional<Long> ontNumber,
#Valid #RequestParam #Min(1) #Max(999) Optional<Integer> model,
...... more parameters);
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint
'javax.validation.constraints.Max' validating type
'java.util.Optional'. Check configuration for
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>

As per documentation : You should add annotation #Validated to your controller, if you're using method argument validation.
Now your code would be like this:
#RestController
#RequestMapping("/your_api")
#Validated
public class YourController {
#GetMapping("/your_endpoint")
public ResponseEntity> getEmps(
#RequestParam #Min(1) #Max(999) Optional deptNUmber){

Related

How to validate Spring #PathVariable properties first?

I have the following PUT method in my Spring Controller class. There is a parameter annotated with #PathVariable. There is another parameter annotated with #RequestBody #Valid. The problem is RequestBody is getting validated first and the method returns in case of exception. But I want #Pathvariable to be validated first and return in case of exception.
#RestController
#Validated
#RequestMapping(value = "/v1/order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderMappingController {
#PutMapping(value = "/{id}/order-mapping", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> UpdateOrderMapping(#PathVariable(value = "id") #NotBlank(message = "InvalidID")
#ValidOrderId String id,
#RequestBody #Valid OrderMappingRequest request) {
//...
}
}
Thanks in Advance!
You can write custom annotation and validator to validate the #Pathvariable
#RestController
#Validated
#RequestMapping(value = "/v1/order", produces = MediaType.APPLICATION_JSON_VALUE)
public class OrderMappingController {
#PutMapping(value = "/{id}/order-mapping", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> UpdateOrderMapping(#PathVariable(value = "id") #OrderId String id,
#RequestBody #Valid OrderMappingRequest request) {
//...
}
}
Create custom annotation OrderId which can validate with custom validator class OrderIdValidator
#Constraint(validatedBy = OrderIdValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.METHOD,PARAMETER })
public #interface OrderId{
public String message() default "order id not found ";
public Class<?>[] groups() default {};
public Class<? extends Payload>[] payload() default{};
}
Then you can write the custom validator class which can check the business logic if the id is present in db or any other validation
public class OrderValidator implements ConstraintValidator<OrderId, String> {
#Autowired
private OrderRepository repository;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return (repository.existsByPeriod(value));
}
}
I suggest to try a different approach. Try not to annotate #Valid on your OrderMappingRequest, then implement a custom org.springframework.validation.Validator for your OrderMappingRequest bean which will then be injected and invoke in your OrderMappingController#UpdateOrderMapping method.
public class OrderMappingController {
#Autowired
private OrderMappingRequestValidator validator
#PutMapping("/{id}/order-mapping")
public ResponseEntity<?> UpdateOrderMapping(#PathVariable
#NotBlank(message = "InvalidID")
#ValidOrderId String id,
#RequestBody OrderMappingRequest request,
BindingResult result) {
validator.validate(request, result);
if(result.hasErrors()){
... do stuff or throw
}
}
}

#Valid working with #ModelAttribute, not with #RequestAttribute

I'm implementing a #RestController and I realized that #Valid is working with #RequestBody or #ModelAttribute params of a #GetMapping method, but not with a #RequestAttribute parameter.
To get validated the #RequestAttribute annotated param I have to annotate my Controller class with #Validated.
Following my code:
Controller
#Log4j2
#RestController
#RequestMapping("/test/api/v1/entity")
public class MyController extends SomeController {
#GetMapping("/getInfo")
public ResponseEntity<<MyResponse>> infoStatus (RequestParam(required = false) String inputStr,
#Valid #RequestAttribute ObjectToValidate objToValidate){
//Any stuff here
}
}
Bean to validate
#Getter
#Setter
#Valid
public class ObjectToValidate {
#NotNull
#NotEmpty
private String anyCode;
}
The result is anyCode is not checked to be not null nor empty.
If I annotate MyController with #Validate, the ObjectToValidate param is validate as expected.
If I change controller as follows, the validation also works.
#Log4j2
#RestController
#RequestMapping("/test/api/v1/entity")
public class MyController extends SomeController {
#ModelAttribute
public ObjectToValidate addToModel(#RequestAttribute ObjectToValidate
objToValidate) { return objToValidate; }
#GetMapping("/getInfo")
public ResponseEntity<MyResponse> infoStatus (
#RequestParam(required = false) String inputStr,
#Valid #ModelAttribute ObjectToValidate objToValidate
){
//Any stuff here
}
}
Please, could you explain why?
#Valid can be used on #RequestBody Controller Method Arguments.
That is #RequestBody method argument can be annotated with #Valid to invoke automatic validation.
It will be no use if you annotate ObjectToValidate class with #Valid.
#PostMapping("/notes")
Note getNote(#Valid #RequestBody Note note) {
return repository.save(note);
}
To validate the path variable, the controller class should be annotated with #Validated
#RestController
#Validated // class level
public class NoteController {
#GetMapping("/note/{id}")
Note findOne(#PathVariable #NotBlank(message = "Id must not be empty") String id) {
return repository.findById(id)
.orElseThrow(() -> new NotekNotFoundException(id));
}
}
Hope it helps!!

Add Custom Validation Annotation for a Parameter in Controller - JSR-303

I can't figure out how to resolve the following use case in Spring Boot. Indeed, I have a Spring Boot Rest Api (eg: user-api) with the following controller method with a custom validator for a parameter :
#PostMapping
public User createUser(#ValidZipCode #RequestBody #Valid User user){
return userService.saveUser(user);
}
The User Class is defined in an external dependency (eg: user-model). It has the following fields :
public class User {
#NotNull
private String firstName;
#NotNull
private String lastName;
private String zipCode;
// getters, setters ..
}
In, user-api I created the following custom annotation :
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ZipCodeValidator.class)
public #interface ValidZipCode {
String message() default "Must be a valid zipCode. Found: ${validatedValue}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And so the ZipCodeValidator implementation :
public class ZipCodeValidator implements ConstraintValidator<ValidZipCode, User> {
private ZipCodeService zipCodeService;
#Override
public void initialize(ValidZipCode constraintAnnotation) { }
#Override
public boolean isValid(User user, ConstraintValidatorContext constraintValidatorContext) {
return !Objects.isNull(user.getZipCode()) ?
zipCodeService.isValidZipCode(user.getZipCode()) :
false;
}
NB: zipCodeService.isValidZipCode() is a simple boolean method.
The problem is that when I call the endpoint it never access the #ValidZipCode annotation. Is there any bean configuration to set up to make it works ?
Thks for your help ;)
UPDATE
Thanks to #cassiomolin for his answer. Indeed, when I annotate the controller class with #Validated It works :D
I Hope this post will help other devs ;)
Ensure that your controller class is annotated with #Validated.
See the following quote from the documentation:
To be eligible for Spring-driven method validation, all target classes need to be annotated with Spring’s #Validated annotation [...]

#RepositoryRestController not recognized

I have the following controller:
#RepositoryRestController
public class TestController {
#RequestMapping(value = "/testables", method = RequestMethod.GET)
public String get(){
return "testin it";
}
}
And it is not picked up by Spring. I get 404 when I hit /apiroot/testables address. If I change it to be #RestController and add the api root to request mapping value, then it works. Also, if I change the mapping to point to "/orders/testables", it works as #RepositoryRestController. I also have the following controller in the same package which works fine:
#RepositoryRestController
public class SendEmailController {
#Autowired
private MessageSource messageSource;
#Autowired
private JavaMailSender javaMailSender;
#Autowired
private OrderRepository orderRepository;
#RequestMapping(value = "/orders/{id}/sendOrderEmailToSupplier")
public void sendOrderEmailToSupplier(#PathVariable("id") Long id, WebRequest request) throws MessagingException {...
#RepositoryRestController deal with basePath only for resources which it manage. In other cases the path should not contain the "base".
If you want to build custom operations underneath basePath, you can use #BasePathAwareController.

jersey #NotNull doesn't check on input

I have a jersey REST service, and I use #NotNull to check on the #pathparam, but it seems not working.I've include jersey-bean-validation in pom.xml
my code is here:
#Path("/resource")
public class MyResource extends AbstractResource {
#POST
#Path("/report")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ResponseClass(ReportAcknowledgement.class)
public Response getGrowthResults(MyRequest request,
#Context HttpHeaders headers) throws Exception {
String organizationId = request.getOrganization(); <-- null here
..
validateOrganization(organizationId);
public abstract class AbstractResource {
..
protected void validateOrganization(#NotNull(message = "{org.string.null}") #Valid String organizationId) throws Exception {
...
}
Ensure you are using the jersey-bean-validation dependency:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.22.2</version>
</dependency>
According to the documentation, having the above mentioned dependency on the classpath will activate the Bean Validation feature on Jersey.
The question is super-old, so you have probably already found an answer...
But you are missing the #Valid annotation on the parameter.
Try this
#POST
#Path("/report")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#ResponseClass(ReportAcknowledgement.class)
public Response getGrowthResults(#javax.validation.Valid MyRequest request,
#Context HttpHeaders headers) throws Exception {
String organizationId = request.getOrganization(); <-- null here
..
validateOrganization(organizationId);
}

Resources