Search code examples
springspring-bootmodel-view-controllerspring-validation

Network calls inside custom validations in spring boot


I need to validate a number of fields in my REST Api. Is there a specific pattern to do this ? My api is cluttered with these validation calls.

  1. Check auth token in the http request against another service to verify access permissions.

  2. Validate a number of fields in request body such as category, type etc by making calls to another service.

  3. Generic null & empty checks. Currently I'm using spring validation to do this.


Solution

  • Check auth token in the http request against another service to verify access permissions

    I would suggest validating the token in a filter before the request reaches the controller.

    Something like this

    1. Create a new token validation filter
        @Component
        public class TokenValidationFilter extends OncePerRequestFilter {
            private TokenValidationService tokenValidationService;
        
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                    throws ServletException, IOException {
        
                final String authorizationHeader = request.getHeader("Authorization");
                
              boolean isValid = tokenValidationService.validate(authorizationHeader);
                //do authentication here 
                chain.doFilter(request, response);
            }
        }
    

    Add the filter to http filters

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // this just an example you might needed to added before a different filter 
        }
    }
    

    Validate a number of fields in request body such as category, type etc by making calls to another service.

    Spring allows creating custom constraints. See spring doc.

    Example: UniqueUsername interface

    @Documented
    @Constraint(validatedBy = UniqueUsernameValidator.class)
    @Target( { ElementType.METHOD, ElementType.FIELD,ElementType.TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface UniqueUsername {
        String message() default "username is not unique";
        Class<?>[] groups() default {};
        Class<? extends Payload>[] payload() default {};
    }
    
    public class UniqueUsernameValidator implements ConstraintValidator<UniqueUsername, UserDto> {
        private final UserService usersService;
    
        @Override
        public void initialize(UniqueUsername constraintAnnotation) {
    
        }
    
        @Override
        public boolean isValid(UserDto value, ConstraintValidatorContext context) {
           return usersService.isUnique(value.getUsername());
        }
    
    }
    

    user dto:

    
    @UniqueUsername(message = "Username is used")
    public class UserDto {
        @DecimalMin(value = "0")
        private long id;
        @NotBlank(message = "Username can not be blank")
        private String username;
    }
    

    Note that the constraints could be class level like UniqueUsername or filed level like NotBlank.

    Generic null & empty checks. Currently I'm using spring validation to do this.

    spring validation/constraints is correct.