I'm using Spring version 4.1.6.RELEASE. I have the following custom ConstraintValidator.
public class CountryCodeValidator implements ConstraintValidator<CountryCode, String> {
private List<String> allowedCountries;
#Autowired
public void setAllowedCountries(#Value("${allowed.countries}")String countries) {
allowedCountries = Arrays.asList(countries.split(","));
}
#Override
public void initialize(CountryCode constraintAnnotation) { }
#Override
public boolean isValid(String countryCode, ConstraintValidatorContext ctx) {
return null != countryCode && allowedCountries.contains(countryCode.toUpperCase());
}
}
and I have the following annotation
#Documented
#Constraint(validatedBy = CountryCodeValidator.class)
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface CountryCode {
String message() default "{CountryCode}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
I'm using this in a request object like the following
#CountryCode(message = MessageKeys.COUNTRY_CODE_INVALID)
private String countryCode;
When I run the application, everything is working as expected. However in the unit test for the controller it is failing.
My unit test code is like the following
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:/test-context.xml")
public class ControllerTest {
#InjectMocks
private Controller controller;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testEndpoint() {
Request request = new Request();
RestAssuredMockMvc.given().standaloneSetup(controller).contentType(MediaType.APPLICATION_JSON_VALUE).body(request)
.post("/endpoint").then().statusCode(HttpStatus.OK.value());
}
}
When I run the code I could not get #Value("${allowed.countries}"), therefore I'm receiving a null pointer exception in the isValid method.
What am I missing?
Thanks in advance
Related
There is a controller accepting code as a path variable
#RestController
#RequestMapping(value = "/api/currency")
#Validated
public class CurrencyController {
#GetMapping("/gif/{code}")
public ResponseEntity<Map> getChangeGif(#PathVariable #Code String code){
// some implementation
return null;
}
}
I want to use my own annotation to validate code as I want
#Target( { FIELD, PARAMETER })
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = CodeValidator.class)
public #interface Code {
public String message() default "error message";
public Class<?>[] groups() default {};
public Class<? extends Payload>[] payload() default {};
}
And here is the validator
public class CodeValidator implements ConstraintValidator<Code, String> {
#Override
public void initialize(Code constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
#Override
public boolean isValid(String code, ConstraintValidatorContext context) {
// validator implementation
return false;
}
}
For some reason when requests come, validation just skipps, and controller continue working without it
I have a Spring Boot Application where I need to perform some validation over the request's fields based on a header value.
So like any other Spring Boot App my entry point in my rest controller looks like
public ResponseEntity<Mono<MyResponse>> myOperation(MyRequest request, String another_parameter)
My problem here is, in order to perform my validations I was thinking on using org.springframework.validation.Validator
Whenever you want to implement above Interface, you have to do something like:
public class WebsiteUserValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return MyRequest.class.equals(clazz);
}
#Override
public void validate(Object obj, Errors errors) {
MyRequest user = (MyRequest) obj;
if (checkInputString(MyRequest.getSomeField())) {
errors.rejectValue("someField", "someField.empty");
}
}
private boolean checkInputString(String input) {
return (input == null || input.trim().length() == 0);
}
}
I would like to get the headers in the validate method implementations.
How to achieve that? (get the headers at any time so to speak).
I think use javax.validation.ConstraintValidator<A extends Annotation, T> will better.
for example
the Annotation
#Target({ElementType.TYPE, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy ={SexConstraintValidator.class} )
public #interface Sex {
//default error message
String message() default "default error message";
//groups
Class<?>[] groups() default {};
//payload
Class<? extends Payload>[] payload() default {};
}
SexConstraintValidator
public class SexConstraintValidator implements ConstraintValidator<Sex,String> {
#Override
public void initialize(Sex constraintAnnotation) {
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
boolean isValid =doSomeValided();
return isValid;
}
}
validate object
public class ValidateObject {
#Sex(message="error message")
private String sex;
// ...
}
the validate method
import org.springframework.validation.annotation.Validated;
public ResponseEntity<Mono<MyResponse>> myOperation(#Validated ValidateObject request, String another_parameter)
or validate manual like this
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Set<ConstraintViolation<ValidateObject>> validate=validatorFactory.getValidator().validate(validateObject);
I am trying to validate a method parameter using custom annotation, but annotation validator is not getting invoked.
The annotation
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = FieldValidator.class)
public #interface ValidField {
String message() default "Incorrect field.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
The FieldValidator
public class FieldValidator implements ConstraintValidator<ValidField, String> {
#Override
public void initialize(final ValidField arg0) {
}
#Override
public boolean isValid(final String field, final ConstraintValidatorContext context) {
System.out.println("Called annotations successfully - "+ field);
return false;
}
}
Testing through the main method
public void testAnnotation(#ValidField String q){
System.out.println("inside testAnnotation..");
}
/************************* TESTING ****************************/
public static void main(String[] args) {
Test test= new Test();
test.testAnnotation("sample");
Expected: Called annotations successfully - sample should be displayed in the console
Okay, It was a mistake testing it by the main method.
#Retention(RetentionPolicy.RUNTIME) says annotation will be available at runtime.
It was tested successfully by the service call when the server is running.
I have created a custom validator to validate the String passed to the function in converter. However, the custom validator is not being called. Am I missing something?
OperationParameter.java
#Documented
#Constraint(validatedBy = OperationParameterValidation.class)
#Target( { ElementType.PARAMETER
})
#Retention(RetentionPolicy.RUNTIME)
public #interface OperationParameter {
String message() default "Operation Parameter Invalid";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
OperationParameterValidation.java
public class OperationParameterValidation implements ConstraintValidator<OperationParameter, String> {
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
System.out.println("Validator called");
// validation process
// return true / false;
}
}
converter.java
#Component
public class StringToOperation implements Converter<String, Operation> {
#Override
public Operation convert(#Valid #OperationParameter String source) {
// Even I pass wrong String this function is executed successfully, and no print from validator
}
}
Service.java
public class Service {
#Autowired
ConversionService conversionService;
public void action() {
String action = "";
Operation addInsertOperation = conversionService.convert(action, Operation.class);
}
}
Set #SupportedValidationTarget(ValidationTarget.PARAMETERS) on validator class
Custom annotation, as defined https://dzone.com/articles/bean-validation-and-jsr-303
Can we use #Capitalized annotation in controller?
e.g.
#RestController
public class Abc {
#RequestMapping(value="/abc", method=RequestMethod.POST)
public String abc(#Capitalized #RequestParam(value="abc") String abc) {
}
}
I used in this way, but it is not working. Any idea why it is not working?
Thanks,
Add Parameter ElementType in annotation target, then it will work.
E.g.:
#Target(ElementType.PARAMETER)
Assuming that #Capitalized is:
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = CapitalizedValidator.class)
#Documented
public #interface Capitalized {
String message() default "should be capital";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and you have a constraint validation impl as:
public class CapitalizedValidator implements ConstraintValidator<Capitalized, String> {
private String message;
#Override
public void initialize(Capitalized constraintAnnotation) {
message = constraintAnnotation.message();
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
String inUpperCase = value.toUpperCase();
if (inUpperCase.equals(value)) {
return true;
}
context.buildConstraintViolationWithTemplate(message);
return false;
}
}
Then try this with you're controller:
#Validated
#RestController
public class SampleController {
#RequestMapping(method = RequestMethod.POST)
public String post(#Capitalized #RequestParam("content") String content) {
return content;
}
}
After including below code in Application.java, Its working fine.
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator());
return methodValidationPostProcessor;
}