11. Exemplo 09 - Conversão e validação de números inteiros
Vamos agora analisar uma série de exemplos sobre a conversão e validação de parâmetros de formulário. O problema é o seguinte. Para processar uma URL do tipo [http://machine:port/.../Action], o controlador [FilterDispatcher] instancia a classe que implementa a ação solicitada e executa um dos seus métodos — por predefinição, o método chamado execute. A chamada a este método execute passa por uma série de interceptores:
![]() |
A lista de interceptores está definida no ficheiro [struts-default.xml] na raiz do arquivo [struts2-core.jar]. A lista de interceptores definida nesse ficheiro é a seguinte:
<interceptor-stack name="defaultStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="debugging"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
Entre os interceptores, há um que é responsável por injetar os valores dos parâmetros que acompanham a solicitação na ação, na forma parami=valores. Sabemos que os valores serão injetados no campo parami da ação através do método setParami, caso este exista. Caso contrário, não ocorre qualquer injeção e não é reportado qualquer erro.
A string parami=values é uma cadeia de caracteres. Até agora, a injeção de valores tem sido realizada em campos parami do tipo String:
Injetar a string valeuri como valor da string parami não apresentou qualquer problema. Se parami não for do tipo String, então valeuri deve ser convertida para o tipo Ti de parami. Este é o problema da conversão. Por exemplo, podemos querer que a idade seja um inteiro e escrever na ação:
Além disso, podemos querer restringir a idade a um intervalo entre 1 e 150. Isto apresenta um problema de validação. O parâmetro parami pode ser convertido para o tipo correto sem necessariamente ser válido. Há, portanto, dois passos a completar. Voltando ao diagrama de processamento de pedidos:
![]() |
Dois interceptores irão tratar da conversão e da validação dos parâmetros, respetivamente. Se qualquer uma das etapas falhar, a solicitação não prossegue para a ação (caminho vermelho acima). O formulário a partir do qual os parâmetros incorretos foram enviados é exibido novamente com mensagens de erro.
Os interceptores envolvidos na conversão e validação de parâmetros são os interceptores conversionError e validation nas linhas 19 e 20 da lista de interceptores apresentada anteriormente. Observe nas linhas 20–22 que o interceptor de validação não é aplicado se o método chamado for um dos métodos input, back, cancel ou browse. Utilizaremos esta propriedade mais adiante.
Começaremos por examinar a conversão e a validação de inteiros. Iremos dedicar algum tempo a este primeiro exemplo, uma vez que a validação envolve muitos elementos. Assim que compreendermos estes, avançaremos mais rapidamente pelos exemplos que se seguem.
11.1. O formulário
![]() |
- em [1], o formulário de entrada
- em [2], o resultado ao validar sem introduzir quaisquer valores
11.2. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
- em [1], as três vistas da aplicação
- em [2], o código-fonte, os ficheiros de mensagens internacionalizadas e os ficheiros de configuração do Struts.
11.3. Configuração do Struts
A aplicação é configurada pelos ficheiros [struts.xml] e [example.xml].
O ficheiro [struts.xml] é o seguinte:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<constant name="struts.custom.i18n.resources" value="messages" />
<include file="example/example.xml"/>
<package name="default" namespace="/" extends="struts-default">
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Accueil</param>
<param name="namespace">/example</param>
</result>
</action>
</package>
</struts>
As linhas 12–18 definem a ação [/example/Home] como a ação padrão quando o utilizador não especifica nenhuma.
O ficheiro [example.xml] é o seguinte:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Accueil">
<result name="success">/example/Accueil.jsp</result>
</action>
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
</package>
</struts>
- Linhas 8–10: A ação [Home] apresenta a vista [Home.jsp]
- linha 11: a ação [FormInt] executa, por predefinição, o método execute da classe [example.FormInt]. Veremos que serão executados outros dois métodos: os métodos input e cancel. Estes métodos serão então especificados nos parâmetros da solicitação.
- linha 12: a chave input exibirá a vista [FormInt.jsp] (linha 5). Esta vista é a vista do formulário.
- linha 13: a chave cancel será devolvida por um método cancel associado ao link [Cancel]. A vista renderizada será então a vista [Home.jsp] após um redirecionamento (type=redirect).
- Linha 14: A chave `success` é devolvida pelo método `execute` da ação [FormInt]. Se a solicitação chegar ao método `execute`, significa que passou com sucesso por todos os interceptores, particularmente aqueles que verificam a validade dos parâmetros. O método `execute` devolve então simplesmente a chave `success`, que irá apresentar a vista de confirmação [ConfirmationInt.jsp].
11.4. Ficheiros de mensagens
O ficheiro [messages.properties] é o seguinte:
Accueil.titre=Accueil
Accueil.message=Struts 2 - Conversions et validations
Accueil.FormInt=Saisie de nombres entiers
Form.titre=Conversions et validations
FormInt.message=Struts 2 - Conversion et validation de nombres entiers
Form.submitText=Valider
Form.cancelText=Annuler
Form.clearModel=Raz mod\u00e8le
Confirmation.titre=Confirmation
Confirmation.message=Confirmation des valeurs saisies
Confirmation.champ=champ
Confirmation.valeur=valeur
Confirmation.lien=Formulaire de test
Além deste ficheiro, as visualizações utilizam o seguinte ficheiro [FormInt.properties]:
int1.prompt=1-Nombre entier positif de deux chiffres
int1.error=Tapez un nombre entier positif de deux chiffres
int2.prompt=2-Nombre entier
int2.error=Tapez un nombre entier
int3.prompt=3-Nombre entier >=-1
int3.error=Tapez un nombre entier >=-1
int4.prompt=4-Nombre entier <=10
int4.error=Tapez un nombre entier <=10
int5.prompt=5-Nombre entier dans l''intervalle [1,10]
int5.error=Tapez un nombre entier dans l''intervalle [1,10]
int6.prompt=6-Nombre entier dans l''intervalle [2,20]
int6.error=Tapez un nombre entier dans l''intervalle [2,20]
O ficheiro [FormInt.properties] só é utilizado quando a ação que gerou a vista é a ação [FormInt]. Esta é uma forma de dividir o ficheiro de mensagens caso este se torne demasiado grande. As mensagens para a ação Action estão localizadas no ficheiro [Action.properties].
11.5. Visualizações e Ações
Vamos agora apresentar as vistas e as ações da aplicação. Com base na configuração da aplicação:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="example" namespace="/example" extends="struts-default">
<action name="Accueil">
<result name="success">/example/Accueil.jsp</result>
</action>
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
</package>
</struts>
Podemos ver que existem três vistas [Home.jsp, FormInt.jsp, ConfirmationFormInt.jsp] e duas ações [Home, FormInt].
11.5.1. Home.jsp
A vista [Home.jsp] é a seguinte:

O seu código é o seguinte:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Accueil.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Accueil.message"/></h2>
<ul>
<li>
<s:url id="url" action="FormInt!input"/>
<s:a href="%{url}"><s:text name="Accueil.FormInt"/></s:a>
</li>
</ul>
</body>
</html>
O link na linha 14 gera o seguinte código HTML:
<a href="<a href="view-source:http://localhost:8084/exemple-09/example/FormInt.action">/exemple-09/example/FormInt!input.action</a>">Saisie de nombres entiers</a>
Trata-se, portanto, de uma ligação para a ação [FormInt] configurada da seguinte forma no ficheiro [example.xml]:
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
Ao clicar na ligação, a classe [example.FormInt] será instanciada e o seu método de entrada será executado. Uma vez que este não existe, será executado o método de entrada da classe pai ActionSupport. Este método não faz nada além de devolver a chave de entrada. Por conseguinte, a vista [/example/FormInt.jsp] será apresentada.
Além disso, o método de entrada é um dos métodos ignorados pelo interceptor de validação:
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
Portanto, não haverá validação de parâmetros. Isto é importante porque não existem parâmetros aqui, e veremos mais tarde que as regras de validação exigem a presença de seis parâmetros.
11.5.2. A ação [FormInt]
A ação [FormInt] está associada à seguinte classe [FormInt]:
package example;
import com.opensymphony.xwork2.ActionSupport;
import com.opensymphony.xwork2.ModelDriven;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
import org.apache.struts2.interceptor.validation.SkipValidation;
public class FormInt extends ActionSupport implements ModelDriven, SessionAware {
// constructor without parameters
public FormInt() {
}
// action model
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormIntModel());
}
return session.get("model");
}
public String cancel() {
// cleaning the model
((FormIntModel) getModel()).clearModel();
// result
return "cancel";
}
@SkipValidation
public String clearModel() {
// close to the model
((FormIntModel) getModel()).clearModel();
// result
return INPUT;
}
// SessionAware
private Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validation
@Override
public void validate() {
// valid int6 input?
if (getFieldErrors().get("int6") == null) {
int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
if (int6 < 2 || int6 > 20) {
addFieldError("int6", getText("int6.error"));
}
}
}
}
Iremos comentar este código conforme necessário. Por enquanto:
- na linha 9, a classe [FormInt] implementa duas interfaces:
- ModelDriven, que possui apenas um método, getModel na linha 16
- SessionAware, que tem apenas um método, setSession na linha 41
- linhas 16–21: implementação da interface ModelDriven. Recorde-se que esta interface permite que o modelo de uma vista seja delegado a uma classe externa, neste caso a seguinte classe [FormIntModel]:
package example;
public class FormIntModel {
// constructor without parameters
public FormIntModel() {
}
// form fields
private String int1;
private Integer int2;
private Integer int3;
private Integer int4;
private Integer int5;
private String int6;
// raz model
public void clearModel(){
int1=null;
int2=null;
int3=null;
int4=null;
int5=null;
int6=null;
}
// getters and setters
...
}
O modelo [FormIntModel] tem seis campos correspondentes aos seis campos de entrada na vista [FormInt.jsp]. Estes seis campos receberão os valores enviados. Quatro deles são do tipo Integer. Por conseguinte, surgirá a questão da conversão de String para Integer para esses campos. O método clearModel permite-lhe reiniciar o modelo.
Voltemos ao método getModel da ação [FormInt]:
// action model
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormIntModel());
}
return session.get("model");
}
- Linhas 3–5: O modelo é recuperado da sessão. Se não estiver presente, é criada uma instância do modelo e colocada na sessão.
- Linha 6: Embora seja criada uma instância da ação a cada nova solicitação feita à ação, o seu modelo permanece na sessão.
Vemos que a classe não define um método de entrada, mas a classe pai possui um que retorna a chave de entrada. A execução deste método exibe a vista [FormInt.jsp], que apresentaremos agora.
11.5.3. A vista [FormInt.jsp]
A vista [FormInt.jsp] é a seguinte:
![]() |
- em [1], o formulário em branco
- em [2], o formulário após a validação de parâmetros incorretos.
O seu código é o seguinte:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Form.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="FormInt.message"/></h2>
<s:form name="formulaire" action="FormInt">
<s:textfield name="int1" key="int1.prompt"/>
<s:textfield name="int2" key="int2.prompt"/>
<s:textfield name="int3" key="int3.prompt"/>
<s:textfield name="int4" key="int4.prompt"/>
<s:textfield name="int5" key="int5.prompt"/>
<s:textfield name="int6" key="int6.prompt"/>
<s:submit key="Form.submitText" method="execute"/>
</s:form>
<br/>
<s:url id="url" action="FormInt" method="cancel"/>
<s:a href="%{url}"><s:text name="Form.cancelText"/></s:a>
<br/>
<s:url id="url" action="FormInt" method="clearModel"/>
<s:a href="%{url}"><s:text name="Form.clearModel"/></s:a>
</body>
</html>
- linhas 12–17: os seis campos de entrada correspondentes aos seis campos do modelo [FormIntModel] para a ação [FormInt]. Quando a vista é apresentada, os atributos value dos campos de entrada são utilizados para os valores apresentados por esses campos. Se o atributo value estiver em falta, então é utilizado o atributo name.
- linha 12: o campo de entrada está associado (name) ao campo int1 da ação ou do seu modelo, caso a ação implemente a interface ModelDriven. É o que acontece aqui. O mesmo se aplica a todos os outros campos.
- Linha 18: O botão [Submit] envia as entradas para a ação [FormInt] definida na linha 11. O seu método execute será executado.
- Linhas 21–22: O link [Cancel] executa o método [FormInt.cancel].
- Linhas 24–25: O link [Clear Model] executa o método [FormInt.clearModel].
11.5.4. A vista [ConfirmationFormInt.jsp]
![]() |
É apresentada quando todas as entradas no formulário [FormInt.jsp] são válidas.
- Em [1], são enviados valores válidos
- Em [2], a página de confirmação
O código para a vista [ConfirmationInt.jsp] é o seguinte:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<html>
<head>
<title><s:text name="Confirmation.titre"/></title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<h2><s:text name="Confirmation.message"/></h2>
<table border="1">
<tr>
<th><s:text name="Confirmation.champ"/></th>
<th><s:text name="Confirmation.valeur"/></th>
</tr>
<tr>
<td><s:text name="int1.prompt"/></td>
<td><s:property value="int1"/></td>
</tr>
<tr>
<td><s:text name="int2.prompt"/></td>
<td><s:property value="int2"/></td>
</tr>
<tr>
<td><s:text name="int3.prompt"/></td>
<td><s:property value="int3"/></td>
</tr>
<tr>
<td><s:text name="int4.prompt"/></td>
<td><s:property value="int4"/></td>
</tr>
<tr>
<td><s:text name="int5.prompt"/></td>
<td><s:property value="int5"/></td>
</tr>
<tr>
<td><s:text name="int6.prompt"/></td>
<td><s:property value="int6"/></td>
</tr>
</table>
<br/>
<s:url id="url" action="FormInt" method="input"/>
<s:a href="%{url}"><s:text name="Confirmation.lien"/></s:a>
</body>
</html>
Para compreender este código, lembre-se de que a vista é apresentada após a instância da classe [FormInt]. Os campos desta classe e do seu modelo [FormIntModel] estão, portanto, acessíveis à vista.
- linhas 16–38: os valores dos seis campos são apresentados
- linhas 42-43: um link para a ação [FormInt]. O código gerado para este link é o seguinte:
<a href="/exemple-09/example/FormInt!input.action">Formulaire de test</a>
O URL específico do link indica que o método de entrada da ação [FormInt] deve processar o pedido. Recorde a configuração da ação [FormInt] em [example.xml]:
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
O método de entrada da classe [FormInt] será o da sua classe pai, ActionSupport. O método de entrada da classe [FormInt] é executado após a execução dos interceptores
![]() |
Sabemos que a chamada ao método de entrada é ignorada pelo interceptor de validação. Portanto, não haverá validação.
A vista [FormInt.jsp] é apresentada:
![]() |
Em [2], os campos de entrada revertem para os seus valores predefinidos. Isto pode parecer normal, mas não é. Uma vez que a ação [FormInt] foi chamada, a classe [FormInt] associada foi instanciada. Como esta classe implementa a interface ModelDriven, o seu método getModel foi chamado:
// action model
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormIntModel());
}
return session.get("model");
}
Podemos ver que o modelo da ação é recuperado da sessão. No passo anterior, este modelo foi atualizado com os valores enviados. Por isso, recuperamos esses valores. Se não tivéssemos colocado o modelo na sessão, teríamos seis campos vazios na vista [FormInt.jsp].
11.5.5. A ação [FormInt!clearModel]
A ação [FormInt!clearModel] é acionada ao clicar no link [Limpar Modelo]:
![]() |
- em [1], o formulário após uma entrada incorreta
- em [2], o formulário após clicar no link [Limpar modelo].
O método [FormInt.clearModel] é o seguinte:
@SkipValidation
public String clearModel() {
// close to the model
((FormIntModel) getModel()).clearModel();
// result
return INPUT;
}
- linha 1: não há validação a realizar. Utilizamos a anotação @SkipValidation para indicar isso. O interceptor de validação não realizará, portanto, nenhuma validação.
- linha 4: o método [FormIntModel].clearModel é executado. Já o encontrámos anteriormente. Este método redefine os seis campos do modelo para null.
- linha 7: o método devolve a chave de entrada.
Voltando à configuração da ação [FormInt]:
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
Podemos ver que a tecla de entrada exibirá a vista [FormInt.jsp]. Esta vista apresenta os seis campos do modelo. Uma vez que estes estão nulos, a vista exibe seis campos vazios [2].
11.5.6. A ação [FormInt!cancel]
A ação [FormInt!cancel] é acionada ao clicar no link [Cancelar]:
![]() |
- em [1], o formulário após uma entrada incorreta
- em [2], a página inicial após clicar no link [Cancelar].
O método [FormInt.cancel] é o seguinte:
public String cancel() {
// cleaning the model
((FormIntModel) getModel()).clearModel();
// result
return "cancel";
}
- linha 1: Note que o método não é precedido pela anotação SkipValidation. No entanto, não queremos realizar validações. O método cancel é um dos quatro métodos (input, back, cancel, browse) ignorados pelo interceptor de validação, pelo que a anotação SkipValidation não é necessária.
- linha 3: isto limpa o modelo
- linha 5: devolve a chave cancel
Voltando à configuração da ação [FormInt]:
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
Podemos ver que a chave "cancel" irá apresentar a vista [Home.jsp] após um redirecionamento do lado do cliente. Isto é mostrado na vista [2].
11.6. O processo de validação
Vamos agora abordar a validação dos seis campos de entrada associados aos seguintes seis campos no modelo:
// champs du formulaire
private String int1;
private Integer int2;
private Integer int3;
private Integer int4;
private Integer int5;
private String int6;
Esta validação ocorre sempre que a classe [FormInt] é instanciada e o método executado não é ignorado pelo interceptor de validação. É controlada pelo:
- o ficheiro [FormInt-validation.xml], se existir na mesma pasta que a classe [FormInt]
- o método [FormInt.validate], se existir.
![]() |
- em [1]: o ficheiro [xwork-validator-1.0.2.dtd] necessário para o processo de validação
- em [2]: o ficheiro [FormInt-validation.xml] na mesma pasta que a classe [FormInt]
O ficheiro [FormInt-validation.xml] é o seguinte:
<!--
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
-->
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//
EN" "http://localhost:8084/exemple-09/example/xwork-validator-1.0.2.dtd">
<validators>
<field name="int1" >
<field-validator type="requiredstring" short-circuit="true">
<message key="int1.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^\d{2}$</param>
<param name="trim">true</param>
<message key="int1.error"/>
</field-validator>
</field>
<field name="int2" >
...
</field>
...
</validators>
- em [3], o URL da DTD (Definição de Tipo de Documento) para o ficheiro de validação. Este deve estar acessível; caso contrário, o ficheiro de validação não será utilizado.
- em [7], o URL da DTD utilizada pela aplicação. Colocámos o ficheiro DTD na pasta [example] do projeto example-09 [1] para que esteja disponível mesmo que não haja acesso à Internet.
- Linhas 11–20: Defina as condições de validação para o parâmetro int1 associado ao campo int1 no modelo.
A tag denominada int1 no formulário é a seguinte:
<s:textfield name="int1" key="int1.prompt" />
O campo int1 no modelo é declarado da seguinte forma:
private String int1;
- Linhas 12–14: verifique se o parâmetro int1 existe (não é nulo) e tem um comprimento diferente de zero. Se não for esse o caso, é exibida uma mensagem de erro associada ao campo de entrada. Está definido em [FormInt.properties] da seguinte forma:
int1.error=Tapez un nombre entier positif de deux chiffres
Se houver um erro, o processo de validação do parâmetro int1 é interrompido (short-circuit=true).
- Linhas 15–19: A validade do parâmetro int1 é verificada utilizando uma expressão regular.
- Linha 16: A expressão regular, neste caso dois dígitos sem nada antes ou depois.
- Linha 17: Antes de ser comparado com a expressão regular, o parâmetro int1 terá os espaços à esquerda e à direita removidos.
- Linha 18: A mensagem de erro, se houver. É a mesma que para o validador anterior.
Vamos ver como isto funciona:
![]() |
- em [1], uma entrada incorreta para o campo int1
- em [2], a página apresentou:
- A mensagem de erro para a chave int1.error é exibida. Está a vermelho.
- O rótulo do campo incorreto também está a vermelho.
- A entrada incorreta é exibida novamente. Isto deve ser antecipado, pois não é necessariamente o comportamento padrão.
Vimos que a validação do formulário aciona a execução do método [FormInt].execute se a solicitação conseguir passar por todos os interceptores, particularmente o interceptor de validação:
![]() |
- Se o pedido chegar ao método `execute` da ação, este devolve a chave `success` ao controlador, como já vimos.
- Se o interceptor de validação interromper a solicitação porque os parâmetros testados são inválidos, então a chave `input` é devolvida ao controlador.
Uma vez que a ação [FormInt] está configurada da seguinte forma:
<action name="FormInt" class="example.FormInt">
<result name="input">/example/FormInt.jsp</result>
<result name="cancel" type="redirect">/example/Accueil.jsp</result>
<result name="success">/example/ConfirmationFormInt.jsp</result>
</action>
Quando ocorre um erro de validação, é apresentada a vista [FormInt.jsp], ou seja, o formulário. As tags Struts foram concebidas para apresentar quaisquer mensagens de erro associadas às mesmas. Veremos, portanto, a vista [FormInt.jsp] com mensagens de erro associadas aos vários campos. Isto é mostrado na vista [2].
Vamos agora examinar a validação do campo int2, declarado da seguinte forma no modelo:
private Integer int2;
A validação do campo int2 em [FormInt-validation.xml] é a seguinte:
<field name="int2" >
<field-validator type="required" short-circuit="true">
<message key="int2.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="int2.error"/>
</field-validator>
</field>
- Linhas 2–4: verifique se o parâmetro int2 existe.
- linhas 5-7: verifique se a conversão de String para Inteiro é possível
- Linhas 3, 6: A mensagem de erro para a chave int2.error é a seguinte:
int2.error=Tapez un nombre entier
A validação para o campo inteiro int3 no modelo em [FormInt-validation.xml] é a seguinte:
<field name="int3" >
<field-validator type="required" short-circuit="true">
<message key="int3.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="int2.error"/>
</field-validator>
<field-validator type="int" short-circuit="true">
<param name="min">-1</param>
<message key="int3.error"/>
</field-validator>
</field>
- Linhas 8–11: verifique se o campo int3 é um inteiro >= -1
- Linhas 3, 7: A mensagem de erro para a chave int3.error é a seguinte:
int3.error=Tapez un nombre entier >=-1
A validação do campo inteiro int4 no modelo em [FormInt-validation.xml] é a seguinte:
<field name="int4" >
<field-validator type="required" short-circuit="true">
<message key="int4.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="int2.error"/>
</field-validator>
<field-validator type="int" short-circuit="true">
<param name="max">10</param>
<message key="int4.error"/>
</field-validator>
</field>
- linhas 8-11: verifique se é um número inteiro <=10
- Linhas 3, 7: A mensagem de erro para a chave int4.error é a seguinte:
int4.error=Tapez un nombre entier <=10
A validação para o campo inteiro int5 no modelo em [FormInt-validation.xml] é a seguinte:
<field name="int5" >
<field-validator type="required" short-circuit="true">
<message key="int5.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="int2.error"/>
</field-validator>
<field-validator type="int" short-circuit="true">
<param name="min">1</param>
<param name="max">10</param>
<message key="int5.error"/>
</field-validator>
</field>
- Linhas 5–9: verifique se o valor é um número inteiro no intervalo [1, 10].
- Linhas 3, 8: A mensagem de erro para a chave int5.error é a seguinte:
int5.error=Tapez un nombre entier dans l''intervalle [1,10]
A validação para o campo String int6 no modelo em [FormInt-validation.xml] é a seguinte:
<field name="int6" >
<field-validator type="requiredstring" short-circuit="true">
<message key="int6.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^\d{1,2}$</param>
<param name="trim">true</param>
<message key="int6.error"/>
</field-validator>
</field>
- Linhas 5–9: verifique se int6 é uma cadeia de caracteres de 2 dígitos.
- Linhas 3, 8: A mensagem de erro para a chave int6.error é a seguinte:
int6.error=Tapez un nombre entier dans l''intervalle [2,20]
A validação anterior não verifica se o parâmetro int6 é um número inteiro no intervalo [2,20]. Esta verificação é realizada no método [FormInt].validate, que é executado após o ficheiro [FormInt-validation.xml] ter sido processado. Este método é o seguinte:
// validation
@Override
public void validate() {
// valid int6 input?
if (getFieldErrors().get("int6") == null) {
int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
if (int6 < 2 || int6 > 20) {
addFieldError("int6", getText("int6.error"));
}
}
}
- Linha 5: Verificamos se existem erros associados ao campo int6. Se houver, paramos aqui.
- linha 6: se não houver erros, recuperamos o campo String int6 do modelo e convertemo-lo num inteiro.
- linha 7: Verificamos se o inteiro recuperado está no intervalo [2,20].
- linha 8: se não for esse o caso, é anexada uma mensagem de erro ao campo int6. Esta mensagem de erro é recuperada do ficheiro de mensagens utilizando a chave int6.error.
Se forem encontrados erros no final deste processo de validação, a chamada ao método [FormInt].execute é interrompida e a chave de entrada é devolvida ao controlador Struts.
![]() |
11.7. Detalhes finais
Vimos várias formas de introduzir números inteiros. Nem todas são equivalentes. Considere, por exemplo, os campos de introdução int5 e int6:
Na vista [FormInt.jsp], estão declarados da seguinte forma:
<s:textfield name="int5" key="int5.prompt"/>
<s:textfield name="int6" key="int6.prompt"/>
O seu modelo é declarado em [FormIntModel.java]:
private Integer int5;
private String int6;
O campo int5 é do tipo Integer, enquanto o campo int6 é do tipo String. As suas regras de validação são diferentes:
<field name="int5" >
<field-validator type="required" short-circuit="true">
<message key="int5.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="int2.error"/>
</field-validator>
<field-validator type="int" short-circuit="true">
<param name="min">1</param>
<param name="max">10</param>
<message key="int5.error"/>
</field-validator>
</field>
<field name="int6" >
<field-validator type="requiredstring" short-circuit="true">
<message key="int6.error"/>
</field-validator>
<field-validator type="regex" short-circuit="true">
<param name="expression">^\d{1,2}$</param>
<param name="trim">true</param>
<message key="int6.error"/>
</field-validator>
</field>
A validação do campo int6 é realizada pelo método validate da ação [FormInt]:
public void validate() {
// valid int6 input?
if (getFieldErrors().get("int6") == null) {
int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
if (int6 < 2 || int6 > 20) {
addFieldError("int6", getText("int6.error"));
}
}
Embora expressas de forma diferente, ambas as regras de validação têm como objetivo verificar se o valor introduzido é um número inteiro dentro de um intervalo especificado. No entanto, o comportamento dos campos int5 e int6 difere em tempo de execução, conforme mostrado nas seguintes capturas de ecrã:
![]() |
- em [1], a mesma entrada incorreta para ambos os campos
- em [2], a página de erro apresentada. Os dois campos apresentam mensagens de erro diferentes.
- Em [3], aparece uma mensagem indesejada para o campo int5 porque está em inglês. Isso resulta da conversão falhada de String para Inteiro. Há também uma exceção nos registos do Apache:
Curiosamente, o Struts procurou um método FormIntModel.setInt5(String value), mas não o encontrou.
A chave para a mensagem de erro é xwork.default.invalid.fieldvalue. Para traduzi-la para francês, basta associar um texto em francês a esta chave. Por isso, adicionamos a seguinte linha ao ficheiro [messages.properties]:
...
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
11.8. Conclusão
Isto conclui o nosso estudo desta primeira aplicação dedicada à validação de parâmetros. Foi complexo de explicar. Vamos agora examinar aplicações semelhantes. Por isso, iremos apenas comentar o que mudou.














