templates.plugins.spincast-validation.spincast-validation.html Maven / Gradle / Ivy
Show all versions of spincast-website Show documentation
{#==========================================
Spincast Properties File Config
==========================================#}
{% extends "../../layout.html" %}
{% block sectionClasses %}plugins plugins-spincast-validation{% endblock %}
{% block meta_title %}Plugins - Spincast Validation{% endblock %}
{% block meta_description %}Validation for your beans/models.{% endblock %}
{% block scripts %}
{% endblock %}
{% block body %}
    
    
    
    
    
    
        
        
        
        
            
            
                
                    
                    Overview
                
	            
	                This plugin provides classes and patterns to help validate your beans/models. 
                
                
                    The validation pattern this plugin promotes is not one using annotations 
                    (like Hibernate's validator, for example). 
                    It targets developers who don't like to pollute their
                    models with too many annotations.
                
                
                    By using this plugin and the mix-ins
                    provided by the Spincast Jackson Json plugin and
                    the Spincast Jackson XML plugin, you can have models
                    which are not bloated with annotations, but that can still be (de)serialized (from/to Json 
                    and XML) and can still be validated properly.
                
             
            
	        
                
                    
                    Installation
                
              
                
                    
                    Add this artifact to your project:
                    
                        
                            
<dependency>
    <groupId>org.spincast</groupId>
    <artifactId>spincast-plugins-validation</artifactId>
    <version>{{spincastCurrrentVersion}}</version>
</dependency> 
                        
                      
                
                
                
                    
                    Then, install the plugin's Guice module, by passing it to the Guice.createInjector(...) method:
                    
                        
                            
Injector guice = Guice.createInjector(
        new AppModule(args),
        new SpincastValidationPluginGuiceModule(IAppRequestContext.class, 
                                                IAppWebsocketContext.class)
        // other modules...
        ); 
                        
                       
                
                
                    ... or by using the install(...) method from your custom Guice module:
                    
                        
                            
public class AppModule extends SpincastDefaultGuiceModule {
    //...
    @Override
    protected void configure() {
        super.configure();
        install(new SpincastValidationPluginGuiceModule(getRequestContextType(), 
                                                        getWebsocketContextType()));
        // other modules...
    }
    
    // ...
} 
                        
                       
                
                
                    
                    When this is in place, you can create validator classes. 
                    For example, a UserValidator, to validate a user:
                
                
                    
                        
                            
public class UserValidator extends SpincastValidatorBase<IUser> {
    //...
    @Override
    protected void validate() {
        // The validation rules go here.
    }
} 
                        
                       
                
                
                    A validator extends the SpincastValidatorBase base class, and parameterizes it
                    with the type of the objects to validate. It overrides the validate()
                    method to define the validation rules. We'll see an example of this in the
                    following Usage section.
                
                
                    
                    You also have to bind a assisted factory for your new validator. 
                    A factory is required because
                    a validator is stateful, it keeps informations about the current object being 
                    validated. Therefore a validator can't be reused and a new instance must 
                    be created for each object to validate, using the factory.
                
                
                    
                        
                            
public class AppModule extends SpincastDefaultGuiceModule {
    public AppModule(String[] mainArgs) {
        super(mainArgs);
    }
    @Override
    protected void configure() {
        super.configure();
        install(new SpincastValidationPluginGuiceModule(getRequestContextType(), 
                                                        getWebsocketContextType()));
        install(new FactoryModuleBuilder().implement(IValidator.class, UserValidator.class)
                                          .build(new TypeLiteral<IValidatorFactory<IUser>>(){}));
        //...
    }
    //...
} 
                        
                       
                
                
                    You can then inject the IValidatorFactory<IUser> where you need to
                    create validators for users. Let's see an example...
                
                
             
            
            
                
                    
                    Usage
                
               
                
                    In this example, we have a UserController controller containing a addUser(...) 
                    route handler that receives POST requests to add new users:
                    
                        
                            
public class UserController implements IUserController {
    private final IUserService userService;
    @Inject
    public UserController(IUserService userService) {
        this.userService = userService;
    }
    protected IUserService getUserService() {
        return this.userService;
    }
    @Override
    public void addUser(IAppRequestContext context) {
        IUser user = context.request().getJsonBody(User.class);
        user = getUserService().addUser(user);
        
        context.response().sendJsonObj(user);
    }
} 
                        
                        
                            Explanation :
                            
                                - 
                                    17 : We deserialize the body of a request we receive 
                                    to a 
IUser object. If the body is not valid Json or if the Json
                                    can't be properly deserialized to a User object, an exception is thrown.
                                 
                                - 
                                    18 : We pass the new user to add to a 
                                    
IUserService service. As we'll see, the validation of the user 
                                    object will be done there!
                                 
                                - 
                                    20 : If the user is valid, and therefore saved in our system,
                                    we can return a new serialized version of it. This new version now contains 
                                    an unique 
id that our system attributed to the new user.
                                 
                            
                          
                       
                
                
                
                    
                    Let's now have a look at the IUserService service, where the user object is validated:
                    
                        
                            
public class UserService implements IUserService {
    private final IUserRepository userRepository;
    private final IValidatorFactory<IUser> userValidatorFactory;
    @Inject
    public UserService(IUserRepository userRepository,
                       IValidatorFactory<IUser> userValidatorFactory) {
        this.userRepository = userRepository;
        this.userValidatorFactory = userValidatorFactory;
    }
    protected IUserRepository getUserRepository() {
        return this.userRepository;
    }
    protected IValidatorFactory<IUser> getUserValidatorFactory() {
        return this.userValidatorFactory;
    }
    @Override
    public IUser addUser(IUser user) {
        IValidator userValidator = getUserValidatorFactory().create(user);
        if(!userValidator.isValid()) {
            String errorMessage = "The user contains errors:\n\n";
            errorMessage += userValidator.getErrorsFormatted(FormatType.PLAIN_TEXT);
            throw new PublicException(errorMessage);
        }
        return getUserRepository().addUser(user);
    }
} 
                        
                        
                            Explanation :
                            
                                - 
                                    8 : We inject the 
IValidatorFactory<IUser>
                                    factory, the object that creates user validators.
                                 
                                - 
                                    24 : We create a validator instance, by passing as a parameter
                                    the 
user object that we want to validate.
                                 
                                - 
                                    25 : By calling 
userValidator.isValid(), we
                                    can know if the validation was successful or not.
                                 
                                - 
                                    27-28 : If some errors occured, we can access each error
                                    separately, but we can also ask the validator to give us a formatted listing of all the errors. 
                                    Here, we ask for a plain text listing, and we use it to build the message we're going to
                                    send in an exception.
                                
 
                                - 
                                    30 : By throwing a 
PublicException exception,
                                    the error message will be available to the end user. Which is desired, since we want to inform
                                    him of what went wrong.
                                 
                                - 
                                    33 : In the other hand, if the validation is successful, 
                                    we add the user to our system! In general, a repository will add the new object to the
                                    appropriated data source, will set the generated 
id on the saved object and
                                    will return this updated object.
                                 
                                
                            
                          
                       
                
                
                    
                    Here is what the validator itself would look like:
                    
                        
                            
public class UserValidator extends SpincastValidatorBase<IUser> {
    @AssistedInject
    public UserValidator(@Assisted IUser user,
                         SpincastValidatorBaseDeps spincastValidatorBaseDeps) {
        super(user, spincastValidatorBaseDeps);
    }
    @Override
    protected void validate() {
        // The name of the user can't be null.
        validateNotNull("name", getObjToValidate().getName());
        // The password of the user should be at least 12 characters long.
        validateMinLength("password", getObjToValidate().getPassword(), 12);
        
        // A custom validation
        if("Stromgol".equalsIgnoreCase(getObjToValidate().getName())) {
            addError("name", 
                     "APP_USER_VALIDATION_ERROR_STROMGOL", 
                     "I know Stromgol very well and you are not him!");
        }
    }
} 
                        
                        
                            Explanation :
                            
                                - 
                                    9-10 : We override the abstract 
validate()
                                    method, which is where we declare the validation rules.
                                 
                                - 
                                    13 : We validate that the user's 
name is 
                                    not null. Validation rules may use methods provided by the SpincastValidatorBase
                                    base class, like validateNotNull(...) in this case. Each of those validation rule 
                                    at least takes a name representing the field that
                                    is validated on the target object, and the value of the field. Also note that the
                                    getObjToValidate() method returns the object to validate (the user in
                                    this case).
                                 
                                - 
                                    16 : We validate that the user's 
password contains
                                    at least 12 characters.
                                 
                                - 
                                    19-23 : We add a custom validation rule. 
                                    Here, the user can't be named "Stromgol". If the custom rule fails, we register an error using the
                                    
addError(...) method. A validation error includes the name of the invalid field, 
                                    a type representing the error, and an error message.
                                 
                            
                          
                       
                
                
                    Finally, don't forget to bind the assisted factory 
                    for your validator, in your custom Guice module. This is what makes possible its injection in the
                    IUserService:
                    
                        
                            
public class AppModule extends SpincastDefaultGuiceModule {
    public AppModule(String[] mainArgs) {
        super(mainArgs);
    }
    @Override
    protected void configure() {
        super.configure();
        install(new SpincastValidationPluginGuiceModule(getRequestContextType(), 
                                                        getWebsocketContextType()));
        install(new FactoryModuleBuilder().implement(IValidator.class, UserValidator.class)
                                          .build(new TypeLiteral<IValidatorFactory<IUser>>(){}));
        //...
    }
    //...
} 
                        
                      
                
             
            
            
            
                
                    
                    The base methods
                
                
                    Every validator implements the IValidator interface which provides
                    those methods:
                
                
                    
                        
                        
                        
- 
    
boolean isValid()
    
        Is the object valid? Was the validation successful?
    
 
- 
    
void revalidate()
    
        Revalidates the object (it may have been modified!). The result of the validation is cached, so it will always
        be the same except if this method is called to clear it.
    
 
- 
    
Map<String, List<IValidationError>> getErrors()
    
        Gets the validation errors. The keys are the names of invalid fields.
    
 
- 
    
List<IValidationError> getErrors(String fieldName)
    
        Gets the validation errors for the specified field.
 
 
    
 
- 
    
String getErrorsFormatted(FormatType formatType)
    
        Gets the formatted errors, if there are any.
        
    
 
- 
    
String getErrorsFormatted(String fieldName, FormatType formatType)
    
        Gets the formatted errors for the specified field, if there are any.
        
    
 
                        
                        
                
                        
 
                    
                 
             
            
            
            
                
                    
                    The default validation methods
                
                
                    By extending SpincastValidatorBase, your validators get access to some default validation
                    methods. As we will see in the
                    next section, we can also define custom validation methods.
                
                
                    The default validation methods all return false if a validation error occures,
                    or true if the validation is successful:
                
                
                    
                        
                        
                        
                            - 
                                
boolean validateNotNull(String fieldName, Object fieldValue)
                                
                                    Validates that a field is not null.
                                
                             
                            - 
                                
boolean validateNotNull(String fieldName, Object fieldValue, String errorMessage)
                                
                                    Validates that a field is not null, and allows a custom error message.
                                
                             
                            
                            - 
                                
boolean validateNotBlank(String fieldName, String fieldValue)
                                
                                    Validates that a field is not blank (null, empty or contain only spaces).
                                
                             
                            - 
                                
boolean validateNotBlank(String fieldName, String fieldValue, String errorMessage)
                                
                                    Validates that a field is not blank (null, empty or contain only spaces), and allows a custom error message.
                                
                              
                            
                            - 
                                
boolean validateMinLength(String fieldName, String fieldValue, int minLength)
                                
                                    Validates that a field has a minimum length.
                                
                             
                            - 
                                
boolean validateMinLength(String fieldName, String fieldValue, int minLength, String errorMessage)
                                
                                     Validates that a field has a minimum length, and allows a custom error message.
                                
                             
                            
                            - 
                                
boolean validateMaxLength(String fieldName, String fieldValue, int maxLength)
                                
                                    Validates that a field has a maximum length.
                                
                             
                            - 
                                
boolean validateMaxLength(String fieldName, String fieldValue, int maxLength, String errorMessage)
                                
                                    Validates that a field has a maximum length, and allows a custom error message.
                                
                             
                            
                            - 
                                
boolean validateMinSize(String fieldName, Integer fieldValue, int minSize)
                                
                                    Validates that a field has a minimum size.
                                
                             
                            - 
                                
boolean validateMinSize(String fieldName, Integer fieldValue, int minSize, String errorMessage)
                                
                                    Validates that a field has a minimum size, and allows a custom error message.
                                
                             
                            
                            - 
                                
boolean validateMaxSize(String fieldName, Integer fieldValue, int maxSize)
                                
                                    Validates that a field has a maximum size.
                                
                             
                            - 
                                
boolean validateMaxSize(String fieldName, Integer fieldValue, int maxSize, String errorMessage)
                                
                                    Validates that a field has a maximum size, and allows a custom error message.
                                
                             
                            
                            - 
                                
boolean validatePattern(String fieldName, String fieldValue, String pattern)
                                
                                    Validates that a field matches a pattern.
                                
                             
                            - 
                                
boolean validatePattern(String fieldName, String fieldValue, String pattern, String errorMessage)
                                
                                    Validates that a field matches a pattern, and allows a custom error message.
                                
                             
                            
                            - 
                                
boolean validatePattern(String fieldName, String fieldValue, String pattern, boolean mustMatch)
                                
                                    Validates that a field matches a pattern or not.
                                    
                                    
                                
                             
                            - 
                                
boolean validatePattern(String fieldName, String fieldValue, String pattern, boolean mustMatch)
                                
                                    Validates that a field matches a pattern or not, and allows a custom error message.
                                    
                                    
                                
                             
                            
                            - 
                                
boolean validateEmail(String fieldName, String fieldValue)
                                
                                    Validates that a field is a valid email address.
                                
                             
                            - 
                                
boolean validateEmail(String fieldName, String fieldValue, String errorMessage)
                                
                                    Validates that a field is a valid email address, and allows a custom error message.
                                
                             
                            
                        
 
                    
                 
                
                    
                    In addition to those validation methods, SpincastValidatorBase
                    also provides methods to add your own errors when you define custom validation rules:
                
                
                    
                        
                        
                            - 
                                
void addError(IValidationError error)
                                
                                    Adds a validation error object directly.
                                
                             
                            - 
                                
void addError(String fieldName, String errorType, String errorMessage)
                                
                                    Adds a validation error by specifying the name of the validated field, the type
                                    of the error and its message.
                                
                             
                            - 
                                
void addErrors(List<IValidationError> errors)
                                
                                    Adds validation errors directly.
                                
                             
                        
 
                    
                
                 
                
             
            
            
            
                
                    
                    Custom validation
                
                
                    In addition to the default validation methods provided by the SpincastValidatorBase
                    base class, you can define your own validation rules.
                
                
                    A custom rules is simply a validation followed by the registration of an error,
                    if the validation fails.
                
                
                    For example, let's say we want to validate that a password contains at least one number. But let's 
                    also say we only want this 
                    error to be registered if the password is not empty in the first place (which is also invalid)! 
                    In other words, we want the resulting validation of an empty password to contain
                    only one error, "Can't be null, empty or only contain spaces.". 
                    We don't want two errors: "Can't be null, empty or only contain spaces." 
                    and "Must contain at least one number!":
                
                
                    
                        
                            
public class UserValidator extends SpincastValidatorBase<IUser> {
    @AssistedInject
    public UserValidator(@Assisted IUser user,
                         SpincastValidatorBaseDeps spincastValidatorBaseDeps) {
        super(user, spincastValidatorBaseDeps);
    }
    @Override
    protected void validate() {
        // Validates that the password is not blank.
        // False is returned if the validation fails.
        boolean passwordValid = validateNotBlank("password", getObjToValidate().getPassword());
        
        // We only validate that the password contains at least one number
        // if it is not blank (if the previous validation succeeded)!
        if(passwordValid) {
            if(getObjToValidate().getPassword().matches(".*\\d+.*")) {
                passwordValid = addError("password", 
                                         "APP_USER_VALIDATION_ERROR_PASSWORD_AT_LEAST_ONE_NUMBER", 
                                         "Must contain at least one number!");
            }
        }
    }
} 
                        
                        
                            Explanation :
                            
                                - 
                                    14 : We first validate that the password
                                    is not blank. If it is blank, the validation fails and 
false
                                    is returned.
                                 
                                - 
                                    18 : We only continue validating the password
                                    if it is not blank in the first place. Of course, we could still run the other validations and have more
                                    errors displayed to the end user: this is up to you.
                                
 
                                - 
                                    19 : Is the password is not empty, we validate 
                                    that it contains at least one number.
                                
 
                                - 
                                    20-22 : If our custom validation fails, we 
                                    register an validation error. A validation error is composed of a 
field name, 
                                    an error type and an error message.
                                 
                            
                          
                       
                
             
            
        
    
 
{% endblock %}    
 Spincast Validation