Spring boot custom annotation for pathVariable - java

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?

Related

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

Validating a lambda request in Micronaut using ConstraintValidator

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

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

How to validate #PathVariable with custom validator annotation containing repository bean

I know how to validate #PathVariable from https://stackoverflow.com/a/35404423/4800811
and it worked as expected with standard annotations but not with the customized one using a Repository bean. Maybe the bean is not initialized and I end up with NullPointerException when accessing the end point has #PathVariable validated. So how to get that work?
My Controller:
#RestController
#Validated
public class CustomerGroupController {
#PutMapping(value = "/deactive/{id}")
public HttpEntity<UpdateResult> deactive(#PathVariable #CustomerGroupEmpty String id) {
}
}
My custom validator:
public class CustomerGroupEmptyValidator implements ConstraintValidator<CustomerGroupEmpty, String>{
#Autowired
private CustomerRepository repository;
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
// NullPointerException here (repository == null)
if (value!=null && !repository.existsByCustomerGroup(value)) {
return false;
}
return true;
}
}
My Custom Annotation:
#Documented
#Constraint(validatedBy = CustomerGroupEmptyValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomerGroupEmpty {
String message() default "The customer group is not empty.";
Class<?>[] groups() default {};
Class<? extends Payload> [] payload() default {};
}
code in this post is correct, only mistake is that validator need to override initialize method as well. Probably user123 incorrect configure repository bean, the simply way to check this is define it manually in configuration class

input validation using custom validation

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

Categories

Resources