Validation is an important topic in any web application, that has to guarantee that only valid data is written to the database. Frameworks solely for this purpose exist, like Apache Commons Validator, however they encourage the use of XML or Annotations.
Mentawai takes a different approach (programmatic approach) by using filters (ValidationFilter and ValidatorFilter) and the org.mentawai.rule.Rule to obtain a similar result with plain Java code. You can download a complete validation example here.
Mentawai offers you two ways of implementing validation in your projects:
ValidationFilter: The validation is done by a validation filter that extends org.mentawai.filter.ValidationFilter. That way the validation is completely decoupled from the action and can be even applied to different actions.
ValidatorFilter: The validation is coded inside the action which can implement the Validatable interface. That way it is not necessary to create a new validation filter for each action and the validation logic is placed inside the action it relates to. The validation is still performed by a filter, but this filter is now a global filter called org.mentawai.filter.ValidatorFilter.
Another detail to note is that despite the fact that the validation mechanism is completely integrated with the i18n functionality, the later can be easily ignored in the case you you don't want your application to be internationalized. To accomplish that, all you need to do is replace the i18n keys for the actual messages you want to display, like we will see in the example below.
In this approach we can code a filter that will take care of the validation logic for some action input fields. Therefore this filter can be applied to any action that needs to get these fields validated before it is executed. Check the example below:
You can validate an action input by creating a special filter which inherits from org.mentawai.filter.ValidationFilter and overriding its prepareValidator() method. All you have to do then is add rules for each field you want to validate, along with the error id you want to show in case of validation failure.
Note that validation happens in a chain for each field. In the example above, the field age has two validation rules: RequiredFieldRule and IntegerRule. The second one is executed only if the first one succeeds. You can add as many rules as you want for a single field. All fields are validated, even if the first field fails to get validated.
By default, validation looks for error messages in the directory /validation inside the document root. The i18n file inside this directory must have the same name of the validation filter, so in our case it is HelloWorldValidator_loc.i18n, where loc is the locale of the file, or example HelloWorldValidator_en_US.i18n. You can also place all your i18n messages inside a single file called master with the method LocaleManager.useMasterI18N(true). By default the master i18n file is /i18n/master_loc.i18n, where loc is the locale of the file, for example master_en_US.i18n.
The contents for the file /validation/HelloWorldValidator_en_US.i18n are below:
What are those %min% and %max% in the error messages? Each rule can place special tokens inside error messages by implementing the method getTokens(). The marker %token% is replaced by these tokens. RequiredFieldRule and EqualRule have no tokens. StringRule and IntegerRule return the min and max values as tokens, so that you can show these values in the error messages, if you want to.
A validation filter is totally decoupled from the action, so the same filter may be used to validate different actions. This avoids unnecessary code duplication and strives for better code maintenance. Below is our ApplicationManager which registers the validation filter for our action:
In the view layer, you can use the handy <mtw:hasError> and <mtw:error field="" /> to display validation error messages. You can also choose to use the <mwt:outError> tag. The contents of the form.jsp file are below:
Although our example works great when it comes to showing the validation error messages, the form is losing all the information on each submition, forcing the user to type everything again. This is a terrible experience for the user and also for the developer who has to code a solution. However Mentawai can do this dirty job automatically for you through its HTML tags.
Starting in version 1.2.1, Mentawai introduced the org.mentawai.validation.Validatable interface. Basically, although creating a validation filter will keep your action simpler and will allow you to re-use the validation schema in different actions, some people have argued that they would rather have everything inside a single class for clarity and also to avoid having to code another class (the validation filter) for simple validation. They have a valid point.
So you can also place the validation schema above inside your action if it is implementing the Validatable interface. Check this new approach below:
Although the validation code is now inside the action, the validation process itself is still done by a filter. When implementing the Validatable interface, you should use the org.mentawai.filter.ValidatorFilter as a global filter. Things you should note about the ValidatorFilter:
- This filter will check whether the action implements Validatable and if yes it will initialize the validator by calling the prepareValidator method and perform the validation on the action input.
- Don't confuse this filter with the ValidationFilter, which is an abstract class described in the section above.
- You will usualy want to use this filter as a global filter, because it will ignore the actions that do not implement Validatable.
- By default, this filter will look for i18n messages in the directory /validation inside a file with the same name of the action, in other words, if you have for example the action HelloWorld.class, the i18n messages will be taken from the file /validation/HelloWorld_loc.i18n. If you are using the master i18n file, through the method LocaleManager.useMasterI18N(true), then the messages will come from the master file: /i18n/master_loc.i18n (ex: master_en_US.i18n).
So now for your ApplicationManager you should have:
Again, if you want to, you can change the default behavior of Mentawai. For validation you can use the methods of the ValidationFilter and ValidatorFilter.
- setResultForError(): By default, the filter returns ERROR when it cannot validate the input. You can change that with this method.
- setDir(): By default, the internal MessageContext looks for i18n files inside the /validation directory. You can change this directory with this method.
- setMessageContext(): If you need to, you can specify a different MessageContext for your filter. Refer to Dynamic Messages for more details on MessageContext.
Creating more rules
When it comes to validation, there is an unlimited number of validation rules you can create. For example you may need to have a CreditCardRule, an URLRule, an EmailRule (provided by Mentawai), a PasswordRule, etc. You can use one of the Mentawai available rules or you can create your owns.
Creating your own rules in Mentawai is very easy and it is just a matter of implementing the org.mentawai.rule.Rule interface. You can also use one of the abstract rules that comes with Mentawai:
- BasicRule:A simple rule to validate a single field. (Ex: StringRule)
- LocaleRule: A rule to validate a single field considering its locale. (Ex: DateRule)
- CrossRule: A rule to compare different fields in the same form for the validation. (Ex: EqualRule)
Mentawai comes with a very useful rule, the org.mentawai.rule.RegexRule. With this rule you can validate a field with an regular expression. Therefore, this rule should be subclassed to create more rules that can be performed with a regular expression. The org.mentawai.rule.EmailRule is such an example.
You should refer to the API documentation for more details on these interfaces.