Skip to content

11. Esempio 09 - Conversione e convalida di numeri interi

Vedremo ora una serie di esempi sulla conversione e la convalida dei parametri dei moduli. Il problema è il seguente. Per elaborare un URL del tipo [http://machine:port/.../Action], il controller [FilterDispatcher] istanzia la classe che implementa l'azione richiesta ed esegue uno dei suoi metodi — per impostazione predefinita, il metodo chiamato execute. La chiamata a questo metodo execute passa attraverso una serie di intercettatori:

L'elenco degli intercettatori è definito nel file [struts-default.xml] nella directory principale dell'archivio [struts2-core.jar]. L'elenco degli intercettatori ivi definiti è il seguente:


             <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>

Tra gli intercettatori, ce n'è uno che ha il compito di inserire i valori dei parametri associati alla richiesta nell'azione nel formato parami=valori. Sappiamo che i valori verranno inseriti nel campo parami dell'azione tramite il metodo setParami, se presente. In caso contrario, non avviene alcun inserimento e non viene segnalato alcun errore.

La stringa parami=values è una stringa di caratteri. Finora, l'inserimento dei valori è stato eseguito nei campi parami di tipo String:

private String parami ;

L'iniezione della stringa valeuri come valore della stringa parami non ha posto alcun problema. Se parami non è di tipo String, allora valeuri deve essere convertito nel tipo Ti di parami. Questo è il problema della conversione. Ad esempio, potremmo volere che l'età sia un numero intero e scrivere nell'azione:

private int age ;

Inoltre, potremmo voler limitare l'età a un intervallo compreso tra 1 e 150. Ciò presenta un problema di convalida. Il parametro parami può essere convertito nel tipo corretto senza essere necessariamente valido. Ci sono quindi due passaggi da completare. Tornando al diagramma di elaborazione della richiesta:

Due intercettatori gestiranno rispettivamente la conversione e la convalida dei parametri. Se uno dei due passaggi fallisce, la richiesta non procede all'azione (percorso rosso sopra). Il modulo da cui sono stati inviati i parametri errati viene visualizzato nuovamente con i messaggi di errore.

Gli intercettatori coinvolti nella conversione e nella convalida dei parametri sono gli intercettatori conversionError e validation alle righe 19 e 20 dell'elenco degli intercettatori mostrato in precedenza. Si noti alle righe 20–22 che l'intercettatore di convalida non viene applicato se il metodo chiamato è uno dei metodi input, back, cancel o browse. Utilizzeremo questa proprietà in seguito.

Inizieremo esaminando la conversione e la convalida dei numeri interi. Dedicheremo un po' di tempo a questo primo esempio perché la convalida coinvolge molti elementi. Una volta compresi questi, passeremo più rapidamente agli esempi che seguono.

11.1. Il modulo

  • in [1], il modulo di immissione dati
  • in [2], il risultato della convalida senza inserire alcun valore

11.2. Il progetto NetBeans

Il progetto NetBeans è il seguente:

  • in [1], le tre viste dell'applicazione
  • in [2], il codice sorgente, i file dei messaggi internazionalizzati e i file di configurazione di Struts.

11.3. Configurazione di Struts

L'applicazione è configurata dai file [struts.xml] e [example.xml].

Il file [struts.xml] è il seguente:


<?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>

Le righe 12–18 definiscono l'azione [/example/Home] come azione predefinita quando l'utente non ne specifica una.

Il file [example.xml] è il seguente:


<?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>
  • Righe 8–10: L'azione [Home] visualizza la vista [Home.jsp]
  • riga 11: l'azione [FormInt] esegue per impostazione predefinita il metodo execute della classe [example.FormInt]. Vedremo che verranno eseguiti altri due metodi: i metodi input e cancel. Questi metodi verranno poi specificati nei parametri della richiesta.
  • riga 12: il tasto input visualizzerà la vista [FormInt.jsp] (riga 5). Questa vista è la vista del modulo.
  • riga 13: la chiave cancel verrà restituita da un metodo cancel associato al link [Cancel]. La vista visualizzata sarà quindi la vista [Home.jsp] dopo un reindirizzamento (type=redirect).
  • Riga 14: la chiave `success` viene restituita dal metodo `execute` dell'azione [FormInt]. Se la richiesta raggiunge il metodo `execute`, significa che ha superato con successo tutti gli intercettatori, in particolare quelli che verificano la validità dei parametri. Il metodo `execute` restituisce quindi semplicemente la chiave `success`, che visualizzerà la vista di conferma [ConfirmationInt.jsp].

11.4. File dei messaggi

Il file [messages.properties] è il seguente:


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

Oltre a questo file, le viste utilizzano il seguente file [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]

Il file [FormInt.properties] viene utilizzato solo quando l'azione che ha generato la vista è l'azione [FormInt]. Questo è un modo per suddividere il file dei messaggi se diventa troppo grande. I messaggi per l'azione Action sono localizzati nel file [Action.properties].

11.5. Viste e azioni

Presenteremo ora le viste e le azioni dell'applicazione. In base alla configurazione dell'applicazione:


<?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>

Possiamo vedere che ci sono tre viste [Home.jsp, FormInt.jsp, ConfirmationFormInt.jsp] e due azioni [Home, FormInt].

11.5.1. Home.jsp

La vista [Home.jsp] è la seguente:

Image

Il codice è il seguente:


<%@ 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>

Il link alla riga 14 genera il seguente codice 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>

Si tratta quindi di un collegamento all'azione [FormInt] configurata come segue nel file [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>

Cliccando sul link verrà istanziata la classe [example.FormInt] e verrà eseguito il suo metodo input. Poiché non esiste, verrà eseguito il metodo input della classe padre ActionSupport. Questo metodo non fa altro che restituire la chiave di input. Pertanto, verrà visualizzata la vista [/example/FormInt.jsp].

Inoltre, il metodo di input è uno dei metodi ignorati dall'intercettatore di validazione:


        <interceptor-ref name="validation">
          <param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>

Pertanto, non ci sarà alcuna convalida dei parametri. Questo è importante perché qui non ci sono parametri e vedremo in seguito che le regole di convalida richiedono la presenza di sei parametri.

11.5.2. L'azione [FormInt]

L'azione [FormInt] è associata alla seguente 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"));
      }
    }
  }
}

Commenteremo questo codice quando necessario. Per ora:

  • riga 9, la classe [FormInt] implementa due interfacce:
  • ModelDriven, che ha un solo metodo, getModel alla riga 16
  • SessionAware, che ha un solo metodo, setSession alla riga 41
  • righe 16–21: implementazione dell'interfaccia ModelDriven. Ricordiamo che questa interfaccia consente di delegare il modello di una vista a una classe esterna, in questo caso la seguente 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
   ...
}

Il modello [FormIntModel] ha sei campi corrispondenti ai sei campi di input nella vista [FormInt.jsp]. Questi sei campi riceveranno i valori inviati. Quattro di essi sono di tipo Integer. Pertanto, per essi si presenterà il problema della conversione da String a Integer. Il metodo clearModel consente di reimpostare il modello.

Torniamo al metodo getModel dell'azione [FormInt]:


  // action model
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormIntModel());
    }
    return session.get("model");
}
  • Righe 3–5: Il modello viene recuperato dalla sessione. Se non è presente, viene creata un'istanza del modello e inserita nella sessione.
  • Riga 6: mentre un'istanza dell'azione viene creata ad ogni nuova richiesta effettuata all'azione, il suo modello rimane nella sessione.

Notiamo che la classe non definisce un metodo di input, ma la classe padre ne possiede uno che restituisce la chiave di input. L'esecuzione di questo metodo visualizza la vista [FormInt.jsp], che presenteremo ora.

11.5.3. La vista [FormInt.jsp]

La vista [FormInt.jsp] è la seguente:

  • in [1], il modulo vuoto
  • in [2], il modulo dopo la convalida dei parametri errati.

Il codice è il seguente:


<%@ 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>

  • righe 12–17: i sei campi di input corrispondenti ai sei campi del modello [FormIntModel] per l'azione [FormInt]. Quando viene visualizzata la vista, gli attributi value dei campi di input vengono utilizzati per i valori visualizzati da questi campi. Se l'attributo value è mancante, viene utilizzato l'attributo name.
  • riga 12: il campo di immissione è associato (name) al campo int1 dell'azione o del suo modello se l'azione implementa l'interfaccia ModelDriven. Questo è il caso qui. Ciò vale anche per tutti gli altri campi.
  • Riga 18: il pulsante [Submit] invia i dati inseriti all'azione [FormInt] definita alla riga 11. Verrà eseguito il suo metodo execute.
  • Righe 21–22: il link [Cancel] esegue il metodo [FormInt.cancel].
  • Righe 24–25: Il link [Clear Model] esegue il metodo [FormInt.clearModel].

11.5.4. La vista [ConfirmationFormInt.jsp]

Viene visualizzata quando tutti i dati inseriti nel modulo [FormInt.jsp] sono validi.

  • In [1] vengono inviati i valori validi
  • In [2], la pagina di conferma

Il codice per la vista [ConfirmationInt.jsp] è il seguente:


<%@ 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>

Per comprendere questo codice, ricorda che la vista viene visualizzata dopo l'istanziazione della classe [FormInt]. I campi di questa classe e del suo modello [FormIntModel] sono quindi accessibili alla vista.

  • righe 16–38: vengono visualizzati i valori dei sei campi
  • righe 42-43: un link all'azione [FormInt]. Il codice generato per questo link è il seguente:


<a href="/exemple-09/example/FormInt!input.action">Formulaire de test</a>

L'URL specifico del collegamento indica che il metodo di input dell'azione [FormInt] deve gestire la richiesta. Ricordiamo la configurazione dell'azione [FormInt] in [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>

Il metodo input della classe [FormInt] sarà quello della sua classe padre, ActionSupport. Il metodo input della classe [FormInt] viene eseguito dopo che sono stati eseguiti gli intercettatori

Sappiamo che la chiamata al metodo di input viene ignorata dall'intercettatore di validazione. Pertanto, non ci sarà alcuna validazione.

Viene visualizzata la vista [FormInt.jsp]:

In [2], i campi di immissione tornano ai valori predefiniti. Questo potrebbe sembrare normale, ma non lo è. Poiché è stata chiamata l'azione [FormInt], è stata istanziata la classe [FormInt] associata. Poiché questa classe implementa l'interfaccia ModelDriven, è stato chiamato il suo metodo getModel:


  // action model
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormIntModel());
    }
    return session.get("model");
}

Possiamo vedere che il modello dell'azione viene recuperato dalla sessione. Nel passaggio precedente, questo modello è stato aggiornato con i valori inviati. Recuperiamo quindi questi valori. Se non avessimo inserito il modello nella sessione, avremmo avuto sei campi vuoti nella vista [FormInt.jsp].

11.5.5. L'azione [FormInt!clearModel]

L'azione [FormInt!clearModel] viene attivata cliccando sul link [Clear Model]:

  • in [1], il modulo dopo un inserimento errato
  • in [2], il modulo dopo aver cliccato sul link [Clear Model].

Il metodo [FormInt.clearModel] è il seguente:


  @SkipValidation
  public String clearModel() {
    // close to the model
    ((FormIntModel) getModel()).clearModel();
    // result
    return INPUT;
}

  • riga 1: non c'è alcuna validazione da eseguire. Usiamo l'annotazione @SkipValidation per indicarlo. L'intercettatore di validazione non eseguirà quindi alcuna validazione.
  • riga 4: viene eseguito il metodo [FormIntModel].clearModel. Lo abbiamo già incontrato. Reimposta i sei campi del modello a null.
  • riga 7: il metodo restituisce la chiave di input.

Tornando alla configurazione dell'azione [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>

Si può notare che, premendo il tasto di invio, verrà visualizzata la vista [FormInt.jsp]. Questa vista mostra i sei campi del modello. Poiché questi sono vuoti, la vista mostra sei campi vuoti [2].

11.5.6. L'azione [FormInt!cancel]

L'azione [FormInt!cancel] viene attivata cliccando sul link [Cancel]:

  • in [1], il modulo dopo un inserimento errato
  • in [2], la pagina iniziale dopo aver cliccato sul link [Annulla].

Il metodo [FormInt.cancel] è il seguente:


  public String cancel() {
    // cleaning the model
    ((FormIntModel) getModel()).clearModel();
    // result
    return "cancel";
}

  • riga 1: Si noti che il metodo non è preceduto dall'annotazione SkipValidation. Tuttavia, non vogliamo eseguire alcuna convalida. Il metodo cancel è uno dei quattro metodi (input, back, cancel, browse) ignorati dall'intercettatore di convalida, quindi l'annotazione SkipValidation non è necessaria.
  • riga 3: questo cancella il modello
  • riga 5: restituisce la chiave cancel

Tornando alla configurazione dell'azione [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>

Possiamo vedere che la chiave "cancel" visualizzerà la vista [Home.jsp] dopo un reindirizzamento lato client. Questo è mostrato nella vista [2].

11.6. Il processo di convalida

Affronteremo ora la convalida dei sei campi di input associati ai seguenti sei campi nel modello:


  // champs du formulaire
  private String int1;
  private Integer int2;
  private Integer int3;
  private Integer int4;
  private Integer int5;
private String int6;

Questa convalida avviene ogni volta che la classe [FormInt] viene istanziata e il metodo eseguito non viene ignorato dall'intercettore di convalida. È controllata dal:

  • il file [FormInt-validation.xml], se presente nella stessa cartella della classe [FormInt]
  • il metodo [FormInt.validate], se esiste.

  • in [1]: il file [xwork-validator-1.0.2.dtd] necessario per il processo di convalida
  • in [2]: il file [FormInt-validation.xml] nella stessa cartella della classe [FormInt]

Il file [FormInt-validation.xml] è il seguente:


<!--
<!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>
  • in [3], l'URL del DTD (Document Type Definition) per il file di validazione. Deve essere accessibile; in caso contrario, il file di validazione non verrà utilizzato.
  • in [7], l'URL del DTD utilizzato dall'applicazione. Abbiamo inserito il file DTD nella cartella [example] del progetto example-09 [1] in modo che sia disponibile anche in assenza di accesso a Internet.
  • Righe 11–20: Imposta le condizioni di validazione per il parametro int1 associato al campo int1 nel modello.

Il tag denominato int1 nel modulo è il seguente:


<s:textfield name="int1" key="int1.prompt" />

Il campo int1 nel modello è dichiarato come segue:


private String int1;
  • Righe 12–14: verificare che il parametro int1 esista (non sia nullo) e abbia una lunghezza diversa da zero. In caso contrario, al campo di immissione viene associato un messaggio di errore. È definito in [FormInt.properties] come segue:

int1.error=Tapez un nombre entier positif de deux chiffres

Se si verifica un errore, il processo di convalida del parametro int1 si interrompe (short-circuit=true).

  • Righe 15–19: la validità del parametro int1 viene verificata utilizzando un'espressione regolare.
  • Riga 16: l'espressione regolare, in questo caso due cifre senza nulla prima o dopo.
  • Riga 17: prima di essere confrontato con l'espressione regolare, al parametro int1 verranno rimossi gli spazi iniziali e finali.
  • Riga 18: il messaggio di errore, se presente. È lo stesso del validatore precedente.

Vediamo come funziona:

  • in [1], un valore errato per il campo int1
  • in [2], la pagina ha restituito:
  • Viene visualizzato il messaggio di errore per la chiave int1.error. È in rosso.
  • Anche l'etichetta del campo errato è in rosso.
  • Viene visualizzato nuovamente il valore errato. Questo deve essere previsto perché non è necessariamente il comportamento predefinito.

Abbiamo visto che la convalida del modulo attiva l'esecuzione del metodo [FormInt].execute se la richiesta riesce a superare tutti gli intercettatori, in particolare l'intercettatore di convalida:

  • Se la richiesta raggiunge il metodo `execute` dell'azione, questa restituisce la chiave `success` al controller, come abbiamo visto.
  • Se l'intercettatore di validazione blocca la richiesta perché i parametri verificati non sono validi, allora la chiave `input` viene restituita al controller.

Poiché l'azione [FormInt] è configurata come segue:


    <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 si verifica un errore di convalida, viene visualizzata la vista [FormInt.jsp], ovvero il modulo. I tag Struts sono progettati per visualizzare eventuali messaggi di errore ad essi associati. Vedremo quindi la vista [FormInt.jsp] con i messaggi di errore allegati ai vari campi. Ciò è mostrato nella vista [2].

Esaminiamo ora la convalida del campo int2, dichiarato come segue nel modello:


private Integer int2;

La convalida del campo int2 in [FormInt-validation.xml] è la seguente:


<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>

  • Righe 2–4: verificare che il parametro int2 esista.
  • Righe 5-7: verificare che la conversione da stringa a numero intero sia possibile
  • Righe 3, 6: il messaggio di errore per la chiave int2.error è il seguente:


int2.error=Tapez un nombre entier

La convalida per il campo intero int3 nel modello in [FormInt-validation.xml] è la seguente:


<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>

  • Righe 8–11: verificare che il campo int3 sia un numero intero >= -1
  • Righe 3, 7: Il messaggio di errore per la chiave int3.error è il seguente:

int3.error=Tapez un nombre entier >=-1

La convalida del campo intero int4 nel modello in [FormInt-validation.xml] è la seguente:


<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>

  • righe 8-11: verifica che sia un numero intero <=10
  • Righe 3, 7: il messaggio di errore per la chiave int4.error è il seguente:


int4.error=Tapez un nombre entier <=10

La convalida per il campo intero int5 nel modello in [FormInt-validation.xml] è la seguente:


<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>

  • Righe 5–9: verifica che il valore sia un numero intero compreso nell'intervallo [1, 10].
  • Righe 3, 8: il messaggio di errore per la chiave int5.error è il seguente:


int5.error=Tapez un nombre entier dans l''intervalle [1,10]

La convalida per il campo String int6 nel modello in [FormInt-validation.xml] è la seguente:


<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>

  • Righe 5–9: verifica che int6 sia una stringa di 2 cifre.
  • Righe 3, 8: il messaggio di errore per la chiave int6.error è il seguente:


int6.error=Tapez un nombre entier dans l''intervalle [2,20]

La convalida precedente non verifica che il parametro int6 sia un numero intero compreso nell'intervallo [2,20]. Questa verifica viene eseguita nel metodo [FormInt].validate, che viene eseguito dopo l'elaborazione del file [FormInt-validation.xml]. Questo metodo è il seguente:


  // 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"));
      }
    }
}
  • Riga 5: Controlliamo se ci sono errori associati al campo int6. In tal caso, ci fermiamo qui.
  • riga 6: se non ci sono errori, recuperiamo il campo String int6 dal modello e lo convertiamo in un intero.
  • riga 7: Verifichiamo che il numero intero recuperato sia compreso nell'intervallo [2,20].
  • riga 8: se così non fosse, al campo int6 viene associato un messaggio di errore. Questo messaggio di errore viene recuperato dal file dei messaggi utilizzando la chiave int6.error.

Se alla fine di questo processo di convalida vengono rilevati degli errori, la chiamata al metodo [FormInt].execute viene interrotta e la chiave di input viene restituita al controller Struts.

11.7. Dettagli finali

Abbiamo visto diversi modi per inserire numeri interi. Non sono tutti equivalenti. Consideriamo, ad esempio, i campi di immissione int5 e int6:

Nella vista [FormInt.jsp], sono dichiarati come segue:


      <s:textfield name="int5" key="int5.prompt"/>
<s:textfield name="int6" key="int6.prompt"/>

Il loro modello è dichiarato in [FormIntModel.java]:


  private Integer int5;
private String int6;

Il campo int5 è di tipo Integer, mentre il campo int6 è di tipo String. Le loro regole di validazione sono diverse:


<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>

La convalida del campo int6 viene eseguita dal metodo validate dell'azione [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"));
      }
}

Sebbene espresse in modo diverso, entrambe le regole di convalida mirano a verificare che il valore immesso sia un numero intero compreso in un intervallo specificato. Tuttavia, il comportamento dei campi int5 e int6 differisce in fase di esecuzione, come mostrato nelle seguenti schermate:

  • in [1], lo stesso valore errato per entrambi i campi
  • in [2], la pagina di errore restituita. I due campi presentano messaggi di errore diversi.
  • In [3], per il campo int5 compare un messaggio indesiderato perché è in inglese. Ciò deriva dalla conversione fallita da stringa a numero intero. C'è anche un'eccezione nei log di Apache:
Avertissement: Error setting expression 'int5' with value '[Ljava.lang.String;@1ad405d8'
ognl.MethodFailedException: Method "setInt5" failed for object example.FormIntModel@21b63266 [java.lang.NoSuchMethodException: example.FormIntModel.setInt5([Ljava.lang.String;)]

Curiosamente, Struts ha cercato un metodo FormIntModel.setInt5(String value) ma non è riuscito a trovarlo.

La chiave del messaggio di errore è xwork.default.invalid.fieldvalue. Per tradurlo in francese, basta associare un testo in francese a questa chiave. Aggiungiamo quindi la seguente riga al file [messages.properties]:


...
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".

11.8. Conclusione

Questo conclude il nostro studio su questa prima applicazione dedicata alla convalida dei parametri. È stato complesso da spiegare. Esamineremo ora applicazioni simili. Pertanto, commenteremo solo ciò che è cambiato.