Search code examples
javaspringspring-bootvalidationspring-websocket

SpringBoot: Validation without @Valid annotation


I have a web socket handler inherited from AbstractWebSocketHandler that handles text messages. My DTOs use javax.validation.constraints for validation. So, in my REST endpoints, I simply can use the @Valid annotation to invoke the validator. However, as far as I know, this annotation is not usable in my web socket handler. How can I invoke the SpringBoot validator programmatically without this annotation?

Besides, is it possible to use the SpringBoot de-serializer for messages instead of JSON.parseObject?

Example:

import javax.validation.constraints.NotBlank;
import lombok.Data;

@Data
class CustomMessage {
    @NotBlank
    private String text;
}
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import lombok.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

@Component
@Slf4j
public class MyCustomWebSocketHandler extends AbstractWebSocketHandler {
    @Override
    protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
        CustomMessage customMessage = JSON.parseObject(message.getPayload(), CustomMessage.class);
        // Validate the message according to javax.validation annotations and throw MethodArgumentNotValidException if invalid
        log.debug("Received valid message {}", customMessage)
    }
}

Solution

  • You will use a Validator to fill a list of ConstraintViolation. An example could looks like this :

    public abstract class GenericService<T> {
    
        protected Validator validator;
    
        protected void validateDomainRecord(T object, String message) {
            Set<ConstraintViolation<T>> violations = validator.validate(object);
            if(!violations.isEmpty()) {
                throw new ConstraintViolationException(message, violations);
            }
        }
    }
    

    In your case, your code will looks something like this :

    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import lombok.NonNull;
    import org.springframework.stereotype.Component;
    import org.springframework.web.socket.handler.AbstractWebSocketHandler;
    
    @Component
    @Slf4j
    public class MyCustomWebSocketHandler extends AbstractWebSocketHandler {
    
        private Validator validator;
        
        @Override
        protected void handleTextMessage(@NonNull WebSocketSession session, @NonNull TextMessage message) {
            CustomMessage customMessage = JSON.parseObject(message.getPayload(), CustomMessage.class);
            // Validate the message according to javax.validation annotations and throw MethodArgumentNotValidException if invalid
            ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
            validator = factory.getValidator();            
            Set<ConstraintViolation<CustomMessage>> violations = validator.validate(customMessage);
            if(!violations.isEmpty()) {
                throw new ConstraintViolationException(message, violations);
            }
    
            log.debug("Received valid message {}", customMessage)
        }
    }
    

    Take look a this good tutorial for more details. I guess it is also possible to customize your validation and your exception too.