Validating a lambda request in Micronaut using ConstraintValidator - java

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;
}
}

Related

#RequestBody as a String is not being validated

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.

Spring boot custom annotation for pathVariable

There is an annotation that should ideally throw an exception if the entity by id is not found for different controllers.
Annotation:
#Target({ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy = CheckExistHandler.class)
public #interface CheckExist {
Class<?> entityClass();
String message() default "Entity with specified id does not exist!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
validator sketches:
#Component
public class CheckExistHandler implements ConstraintValidator<CheckExist, Long> {
#PersistenceContext
private EntityManager entityManager;
#Override
public boolean isValid(Long value, ConstraintValidatorContext context) {
if (value != 0 && entityManager.find(Topic.class, value) != null) {
return true;
} else {
return false;
}
}
}
Method for tests from one controller:
#GetMapping("/{topicId}")
public ResponseEntity<TopicDto> getTopicById(#CheckExist(entityClass = Topic.class) #PathVariable("topicId") Long topicId) {
if (!topicService.isExistByKey(topicId)) {
throw new NotFoundException("topic not found");
}
return new ResponseEntity<>(topicMapper.toDto(topicService.getByKey(topicId)), HttpStatus.OK);
}
In this regard, questions:
How to isolate a class from an annotation in a validator using reflection in order to correctly use the EntityManager?
How to throw an exception without getting 500?

Custom validation for #PathVariable

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

How to get parameter of my Annotation as a validator?

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();
}

Custom parameter validator in spring boot

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

Categories

Resources