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
Related
I do have simple controller which accept String as RequestBody
#RequestMapping(value = "/test", method = RequestMethod.POST)
public ResponseEntity doSmth(#RequestBody #ValidTest String val) {
//do something
return ResponseEntity
.status(HttpStatus.OK)
.body("saved");
}
But for some reason val param not being validated with TestConstraintValidator.class
#Documented
#Constraint(validatedBy = TestConstraintValidator.class)
#Target({PARAMETER})
#Retention(RUNTIME)
public #interface ValidTest{
String message() default "Invalid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Is there even option to validate this? Or do I need to wrap this String withing custom class. And validate it there?
public class TestConstraintValidator implements ConstraintValidator<ValidTest, String> {
#Override
public void initialize(ValidTest constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
#Override
public boolean isValid(String val, ConstraintValidatorContext constraintValidatorContext) {
return false;
}
}
Make sure the controller class is marked as #Validated.
I have a handler like this and a custom annotation #ValidRequest:
#Introspected
public class MessageHandler extends MicronautRequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
#Override
public APIGatewayProxyResponseEvent execute(#ValidRequest APIGatewayProxyRequestEvent event) {
return new APIGatewayProxyResponseEvent()
.withStatusCode(200)
.withHeaders(Collections.singletonMap("Content-Type", "application/json"))
.withBody("OK");
}
}
The annotation itself looks like this:
#Retention(RUNTIME)
#Constraint(validatedBy = {ValidRequestValidator.class})
public #interface ValidRequest {
String message() default "Request is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And the validator is like this:
#Introspected
public class ValidRequestValidator implements ConstraintValidator<ValidRequest, APIGatewayProxyRequestEvent> {
#Override
public boolean isValid(
#Nullable APIGatewayProxyRequestEvent event,
#NonNull AnnotationValue<ValidRequest> annotationMetadata,
#NonNull ConstraintValidatorContext context
) {
if (event == null || event.getBody() == null || event.getBody().isEmpty()) {
throw new RuntimeException("Incorrect request event");
}
return true;
}
}
The problem is that validation is completely ignored. I can send any events with or without body and everything works without exception. I did everything according to the Micronout documentation, what could be wrong?
https://docs.micronaut.io/latest/guide/index.html#beanValidation
Please remove the #Introspect from your validator and try to follow the same as I have done below.
#POST
#Consumes({MediaType.APPLICATION_JSON})
#Produces(MediaType.APPLICATION_JSON)
#Path("/somePath")
public Response workingWithSubscription( #Valid UpdateSubscription updateSubscription) {
#ValidUpdateSubscription
public class UpdateSubscription implements UpdateSubscriptionRequest {
}
#Target({TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = UpdateSubscriptionValidator.class)
#Documented
public #interface ValidUpdateSubscription {
int ERROR_CODE = 1111;
String message() default "Null value";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int errorCode() default ERROR_CODE;
Response.Status status() default Response.Status.BAD_REQUEST;
}
#Named
#ApplicationScoped
public class UpdateSubscriptionValidator implements ConstraintValidator<ValidUpdateSubscription, UpdateSubscription> {
#Override
public boolean isValid(UpdateSubscription value, ConstraintValidatorContext context) {
return true/OR/false;
}
}
I created my own Annotation to validate my REST parameter like this:
#PostMapping("/users")
#ResponseStatus(HttpStatus.CREATED)
public User createUser(#UserConstraint("CREATE_MODE") #RequestBody User user)
{ //code }
I got everything working where my ConstraintValidator is called to validate the User input, but I can't figure out how to get the parameter of my own annotation. I want to read the value CREATE_MODE.
#Constraint(validatedBy = UserValidator.class)
#Target(ElementType.PARAMETER )
#Retention(RetentionPolicy.RUNTIME)
public #interface UserConstraint {
String message() default "";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
public String value();
}
How to access??
public class UserValidator implements ConstraintValidator<UserConstraint, User> {
#Override
public boolean isValid(User user,
ConstraintValidatorContext context) {
???
}
#Override
public void initialize(UserConstraint annotation) {
// initialization, probably not needed
mode = annotation.value();
}
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;
}