Search code examples
javaspring-bootspring-annotationsannotation-processing

Composing validation annotations into a single annotation


I have the below method which uses nameId as a RequestHeader and there are other methods that use the same variable declaration. Is there a way that the nameId validations can be grouped into an interface so that we can annotate instead of adding @Valid & @Pattern every time?

public ResponseEntity<PlanResponse> updatePlanProductByPlanId(
        @RequestHeader(value = "name_id", required = true) @Valid @Pattern(regexp = REGEX_PATTERN_ALPHANUMERIC, message = "{error.invalid.name.id.header}") String nameId,
        @PathVariable(value = "planid") @Valid @Pattern(regexp = REGEX_PATTERN_ONLY_NUMBERS, message = "{error.invalid.plan.id}") String planId,
        @Validated({
            OnPlanProductUpdate.class }) @RequestBody(required = true) SavePlanProductRequest updateProductRequest)
        throws ServiceException { ... }

Expecting something like @NameId annotation.

public ResponseEntity<PlanResponse> updatePlanProductByPlanId(
        @RequestHeader(value = "name_id", required = true) @NameId String nameId,
        @PathVariable(value = "planid") @Valid @Pattern(regexp = REGEX_PATTERN_ONLY_NUMBERS, message = "{error.invalid.plan.id}") String planId,
        @Validated({
            OnPlanProductUpdate.class }) @RequestBody(required = true) SavePlanProductRequest updateProductRequest)
        throws ServiceException { ... }

I tried creating an interface like the one below, but the validation didn't seem to work.

@Valid 
@Pattern(regexp = REGEX_PATTERN_ALPHANUMERIC, message = "{error.invalid.name.id.header}") 
@Retention(RetentionPolicy.RUNTIME)
public @interface NameId {
       String nameId() default "";
}

Solution

  • This should work

    @Pattern(regexp = REGEX_PATTERN_ALPHANUMERIC, message = "{error.invalid.name.id.header}") 
    @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
    @Retention(RUNTIME)
    @Documented
    @Constraint(validatedBy = {})
    public @interface NameId {
           String nameId() default "";
    }
    

    I think the Constraint was missing, and the Valid annotation is not necessary.

    Read more here: https://www.baeldung.com/java-bean-validation-constraint-composition