11. Exemplo 09 - Conversão e validação de números inteiros
Abordamos agora uma série de exemplos sobre a conversão e a validação dos parâmetros de um formulário. O problema é o seguinte. Para processar um 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 denominado 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 aí definida é 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 se encarrega de inserir na ação os valores valeuri dos parâmetros parami que acompanham o pedido na forma parami=valeuri. Sabe-se que valeuri será injetado 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 é sinalizado qualquer erro.
A cadeia parami=valeuri é uma cadeia de caracteres. Até agora, a inserção de valeuri foi efetuada nos campos parami do tipo String:
A injeção da cadeia valeuri como valor da cadeia parami não apresentou qualquer problema. Se parami não for do tipo String, então valeuri deve ser convertido do tipo parami para o tipo Ti. Este é o problema da conversão. Por exemplo, se quisermos que uma idade seja um número inteiro, escreveremos na ação:
Por outro lado, podemos querer limitar a idade entre 1 e 150. Temos aqui um problema de validação. O parâmetro parami pode ser convertido para o tipo correto sem, no entanto, ser válido. Há, portanto, duas etapas a seguir. Se voltarmos ao esquema de processamento de um pedido:
![]() |
Dois interceptores irão encarregar-se, respetivamente, da conversão e da validação dos parâmetros. Se uma das etapas falhar, a solicitação não prossegue o seu percurso até à ação (circuito vermelho acima). O formulário a partir do qual foram enviados os parâmetros errados é exibido novamente com mensagens de erro.
Os interceptores responsáveis pela conversão e validação dos parâmetros são os interceptores conversionError e validation, nas linhas 19 e 20 da lista de interceptores apresentada anteriormente. Note-se, nas linhas 20-22, que o interceptor validation não é aplicado se o método chamado for um dos métodos input, back, cancel, browse. Iremos utilizar esta propriedade mais adiante.
Começamos por estudar a conversão e a validação de números inteiros. Vamos dedicar algum tempo a este primeiro exemplo, uma vez que a validação envolve vários elementos. Depois de os conhecermos, avançaremos mais rapidamente nos exemplos que se seguem.
11.1. O formulário
![]() |
- em [1], o formulário de introdução de dados
- em [2], o resultado quando se valida sem introduzir valores
11.2. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
- em [1], as três vistas da aplicação
- em [2], os códigos-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/Accueil] como ação por predefiniçã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 [Accueil] faz com que seja apresentada a vista [Accueil.JSP]
- linha 11: a ação [FormInt] executa, por predefinição, o método execute da classe [example.FormInt]. Veremos que serão executados mais dois métodos: os métodos input e cancel. Estes métodos serão então especificados nos parâmetros da consulta.
- linha 12: a chave input fará com que seja apresentada a vista [FormInt.JSP] (linha 5). Esta vista corresponde ao formulário.
- linha 13: a chave cancel será devolvida por um método cancel associado ao link [Annuler]. A vista apresentada será, então, a vista [Accueil.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, nomeadamente aqueles que verificam a validade dos parâmetros. O método execute limita-se então a devolver a chave success, que irá apresentar a vista de confirmação [ConfirmationInt.JSP].
11.4. Os 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
Para além deste ficheiro, as vistas 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 segmentar o ficheiro de mensagens caso este seja demasiado grande. As mensagens da ação «Ação» são internacionalizadas no ficheiro [Action.properties].
11.5. As vistas e as ações
Apresentamos agora as vistas e as ações da aplicação. De acordo com a 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>
vemos que existem três vistas [Accueil.JSP, FormInt.JSP, ConfirmationFormInt.JSP] e duas ações [Accueil, FormInt].
11.5.1. Accueil.JSP
A vista [Accueil.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 da 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 um link para a ação [FormInt], configurada da seguinte forma 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>
Ao clicar no link, será instanciada a classe [example.FormInt] e executado o método input dessa classe. Como esta não existe, será executado o método input da classe pai ActionSupport. Este método não faz nada além de devolver a chave input. Assim, será apresentada a vista [/example/FormInt.JSP].
Por outro lado, o método input é um dos métodos ignorados pelo interceptor de validação:
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
Por isso, não haverá validação de parâmetros. Isto é importante porque, neste caso, não existem parâmetros e veremos mais adiante que as regras de validação irão exigir a existência 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 {
// construtor sem parâmetros
public FormInt() {
}
// modelo da ação
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormIntModel());
}
return session.get("model");
}
public String cancel() {
// limpar o modelo
((FormIntModel) getModel()).clearModel();
// resultado
return "cancel";
}
@SkipValidation
public String clearModel() {
// reinicialização do modelo
((FormIntModel) getModel()).clearModel();
// resultado
return INPUT;
}
// SessionAware
private Map<String, Object> session;
public void setSession(Map<String, Object> session) {
this.session = session;
}
// validação
@Override
public void validate() {
// entrada int6 válida?
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 à medida que for necessário. Por enquanto:
- na linha 9, a classe [FormInt] implementa duas interfaces:
- ModelDriven, que tem apenas um método, getModel da 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 transferir o modelo de uma vista para uma classe externa, neste caso a seguinte classe [FormIntModel]:
package example;
public class FormIntModel {
// construtor sem parâmetros
public FormIntModel() {
}
// campos do formulário
private String int1;
private Integer int2;
private Integer int3;
private Integer int4;
private Integer int5;
private String int6;
// modelo vazio
public void clearModel(){
int1=null;
int2=null;
int3=null;
int4=null;
int5=null;
int6=null;
}
// getters e setters
...
}
O modelo [FormIntModel] tem seis campos que correspondem aos seis campos de introdução de dados da vista [FormInt.JSP]. São estes seis campos que irão receber os valores enviados. Quatro deles têm o tipo Integer. Para esses campos, coloca-se, portanto, o problema da conversão de String para Integer. O método clearModel permite reinicializar o modelo.
Voltemos ao método getModel da ação [FormInt]:
// modelo da ação
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormIntModel());
}
return session.get("model");
}
- linhas 3-5: o modelo é procurado na 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 permanecerá na sessão.
Verificamos que a classe não define o método input, mas a classe pai possui um que devolve a chave input. A execução deste método leva à exibição da vista [FormInt.JSP], que apresentamos 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 errados.
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 introdução de dados correspondentes aos seis campos do modelo [FormIntModel] da ação [FormInt]. Ao apresentar a vista, são utilizados os atributos value dos campos de introdução de dados para determinar o valor apresentado por esses campos. Na ausência do atributo value, é 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. É esse o caso aqui. O mesmo se aplica a todos os outros campos.
- linha 18: o botão [Valider] envia os dados introduzidos para a ação [FormInt] definida na linha 11. Será executado o seu método execute.
- linhas 21-22: o link [Annuler] executa o método [FormInt.cancel].
- linhas 24-25: o link [Raz modèle] executa o método [FormInt.clearModel].
11.5.4. A vista [ConfirmationFormInt.JSP]
![]() |
É apresentada quando todas as entradas do formulário [FormInt.JSP] são válidas.
- no [1], são lançados valores válidos
- na [2], a página de confirmação
O código da 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, é necessário ter em conta que a vista é apresentada após a instanciação da classe [FormInt]. Os campos desta classe e do seu modelo [FormIntModel] estão, portanto, acessíveis à vista.
- linhas 16-38: são apresentados os valores dos seis campos
- linhas 42-43: uma ligação para a ação [FormInt]. O código gerado para esta ligação é o seguinte:
<a href="/exemple-09/example/FormInt!input.action">Formulaire de test</a>
O código específico URL do link indica que o método input da ação [FormInt] deve processar o pedido. Recorde-se 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 input da classe [FormInt] será o da sua classe pai ActionSupport. A execução do método input da classe [FormInt] ocorre após a execução dos interceptores
![]() |
Sabe-se que a chamada ao método input é ignorada pelo interceptor de validação. Por conseguinte, não haverá validação.
É apresentada a vista [FormInt.JSP]:
![]() |
Em [2], os campos de introdução de dados recuperam os seus valores introduzidos. Isto pode parecer normal, mas não o é. Uma vez que a ação [FormInt] foi chamada, a classe associada [FormInt] foi instanciada. Como esta classe implementa a interface ModelDriven, o seu método getModel foi chamado:
// modelo da ação
public Object getModel() {
if (session.get("model") == null) {
session.put("model", new FormIntModel());
}
return session.get("model");
}
Vemos que o modelo da ação é recuperado da sessão. Na etapa anterior, esse modelo tinha sido atualizado com os valores enviados. Assim, 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 na ligação [Raz modèle]:
![]() |
- em [1], o formulário após uma introdução de dados incorreta
- em [2], o formulário após um clique no link [Raz modèle].
O método [FormInt.clearModel] é o seguinte:
@SkipValidation
public String clearModel() {
// reinicialização do modelo
((FormIntModel) getModel()).clearModel();
// resultado
return INPUT;
}
- linha 1: não há validação a efetuar. Utiliza-se a notação @SkipValidation para o indicar. O interceptor de validação não efetuará, então, as validações.
- linha 4: o método [FormIntModel].clearModel é executado. Já o encontrámos anteriormente. Este método reinicializa os seis campos do modelo para null.
- linha 7: o método devolve a chave input.
Se voltarmos à 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>
vemos que a chave input irá apresentar a vista [FormInt.JSP]. Esta apresenta os seis campos do modelo. Como estes correspondem a null, a vista apresenta seis campos vazios [2].
11.5.6. A ação [FormInt!cancel]
A ação [Formint!cancel] é acionada ao clicar na ligação [Annuler]:
![]() |
- em [1], o formulário após uma introdução de dados incorreta
- em [2], a página inicial após clicar na ligação [Annuler].
O método [FormInt.cancel] é o seguinte:
public String cancel() {
// limpa-se o modelo
((FormIntModel) getModel()).clearModel();
// resultado
return "cancel";
}
- linha 1: note-se que o método não é precedido pela anotação SkipValidation. No entanto, não se pretende efetuar as validações. O método cancel faz parte dos quatro métodos input, back, cancel e browse, que são ignorados pelo interceptor de validação; por isso, a anotação SkipValidation também não é necessária.
- linha 3: esvazia o modelo
- linha 5: atribui a chave cancel
Se voltarmos à 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>
vemos que a chave cancel irá apresentar a vista [Accueil.JSP] após um redirecionamento do cliente. É isso que mostra a vista [2].
11.6. O processo de validação
Passamos agora à validação dos seis campos de introdução de dados associados aos seis campos seguintes do modelo:
// campos do formulário
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], caso exista na mesma pasta que a classe [FormInt]
- pelo método [FormInt.validate], caso exista.
![]() |
- 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/exemplo-09/exemplo/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 do Tipo de Documento) do ficheiro de validação. Este deve estar acessível; caso contrário, o ficheiro de validação não é utilizado.
- em [7], o URL do DTD utilizado pela aplicação. Colocámos o ficheiro DTD na pasta [example] do projeto exemple-09 [1] para o termos disponível mesmo que não haja acesso à Internet.
- linhas 11-20: definem as condições de validação do parâmetro int1 associado ao campo int1 do modelo.
A baliza denominada int1 no formulário é a seguinte:
<s:textfield name="int1" key="int1.prompt" />
O campo int1 do modelo é declarado da seguinte forma:
private String int1;
- linhas 12-14: verificam se o parâmetro int1 existe (não null) e se tem comprimento diferente de zero. Se não for esse o caso, é associada uma mensagem de erro ao campo de introdução de dados. 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 por uma expressão regular.
- linha 16: a expressão regular, neste caso, 2 algarismos sem nada à frente nem atrás.
- linha 17: o parâmetro int1, antes de ser comparado com a expressão regular, será limpo dos espaços iniciais e finais.
- linha 18: a eventual mensagem de erro. É a mesma que para o validador anterior.
Vamos ver como fica:
![]() |
- em [1], uma entrada incorreta para o campo int1
- em [2], a página apresentada:
- a mensagem de erro da chave int1.error está presente. Está a vermelho.
- o nome do campo com erro também está a vermelho.
- A entrada incorreta é exibida novamente. É necessário ter isto em conta, pois não é necessariamente o comportamento por predefinição.
Vimos que a validação do formulário provoca a execução do método [FormInt].execute se o pedido conseguir passar por todos os interceptores, nomeadamente o de validação:
![]() |
- se a solicitação chegar ao método execute da ação, este devolve a chave success ao controlador, tal como vimos...
- Se o interceptor de validação interromper a solicitação porque os parâmetros verificados não são vá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, é a vista [FormInt.JSP] que é apresentada, ou seja, o formulário. As tags Struts são concebidas de forma a exibirem as eventuais mensagens de erro que lhes estão associadas. Assim, teremos a vista [FormInt.JSP] com as mensagens de erro associadas aos diferentes campos. É isso que mostra a vista [2].
Analisemos agora 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: verificam se o parâmetro int2 existe.
- linhas 5-7: verificam se a conversão de String para Integer é possível
- linhas 3 e 6: a mensagem de erro da chave int2.error é a seguinte:
int2.error=Tapez un nombre entier
A validação dos campos Integer e int3 do 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: verificam se o campo int3 é do tipo inteiro >=-1
- linhas 3 e 7: a mensagem de erro da chave int3.error é a seguinte:
int3.error=Tapez un nombre entier >=-1
A validação dos campos Integer e int4 do 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: verificam se é do tipo inteiro <=10
- linhas 3 e 7: a mensagem de erro da chave int4.error é a seguinte:
int4.error=Tapez un nombre entier <=10
A validação do campo Integer int5 do 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: verificam se é do tipo inteiro no intervalo [1, 10].
- linhas 3 e 8: a mensagem de erro da chave int5.error é a seguinte:
int5.error=Tapez un nombre entier dans l''intervalle [1,10]
A validação do campo String int6 do 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: verificam se int6 é uma cadeia de 2 dígitos.
- linha 3, 8: a mensagem de erro da 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 é efetuada no método [FormInt].validate, que é executado após o ficheiro [FormInt-validation.xml] ter sido processado. Este método é o seguinte:
// validação
@Override
public void validate() {
// entrada int6 válida?
if (getFieldErrors().get("int6") == null) {
int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
if (int6 < 2 || int6 > 20) {
addFieldError("int6", getText("int6.error"));
}
}
}
- linha 5: verifica-se se existem erros associados ao campo int6. Se sim, o processo não prossegue.
- linha 6: se não tiver havido erros, recupera-se o campo String int6 do modelo e transforma-se-o num inteiro.
- linha 7: verifica-se se o inteiro recuperado está dentro do intervalo [2,20].
- linha 8: se não for o caso, é anexada uma mensagem de erro ao campo int6. Esta mensagem de erro é procurada no ficheiro de mensagens com a chave int6.error.
Se, no final deste processo de validação, houver erros, a chamada ao método [FormInt].execute é interrompida e a chave input é devolvida ao controlador Struts.
![]() |
11.7. Últimos detalhes
Vimos várias formas de introduzir números inteiros. Nem todas são equivalentes. Consideremos, 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 está 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 é complementada pelo método validate da ação [FormInt]:
public void validate() {
// entrada int6 válida?
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 campo introduzido é um número inteiro compreendido num intervalo. No entanto, o comportamento dos campos int5 e int6 é diferente na execução, como mostram as seguintes capturas de ecrã:
![]() |
- em [1], a mesma entrada incorreta para ambos os campos
- no [2], a página de erros apresentada. Os dois campos apresentam mensagens de erro diferentes.
- em [3], aparece para o campo int5 uma mensagem indesejada, porque está em inglês. Esta mensagem resulta da conversão falhada de String para Integer. Além disso, existe uma exceção nos registos do Apache:
Curiosamente, o Struts procurou um método FormIntModel.setInt5(String value) que não encontrou.
A chave da mensagem indesejada é xwork.default.invalid.fieldvalue. Para a traduzir para francês, basta associar um texto em francês a essa chave. Assim, adicionamos ao ficheiro [messages.properties] a seguinte linha:
...
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
11.8. Conclusion
Conclui-se assim o estudo desta primeira aplicação dedicada à validação de parâmetros. Foi complexo de explicar. Vamos agora estudar aplicações semelhantes. Por isso, apenas comentaremos o que muda.















