Skip to content

11. مثال ۰۹ – تبدیل و اعتبارسنجی اعداد صحیح

اکنون به مجموعه‌ای از مثال‌ها در مورد تبدیل و اعتبارسنجی پارامترهای فرم می‌پردازیم. مسئله به این صورت است. برای پردازش یک URL از فرم [http://machine:port/.../Action]، کنترل‌کننده [FilterDispatcher] کلاس پیاده‌سازی‌کننده اقدام درخواستی را نمونه‌سازی کرده و یکی از متدهای آن را اجرا می‌کند؛ به‌طور پیش‌فرض این متد با نام execute است. فراخوانی این متد، execute، از طریق مجموعه‌ای از میان‌گیرها (interceptors) عبور می‌کند:

فهرست اینترسپتورها در فایل [struts-default.xml] در ریشهٔ آرشیو [struts2-core.jar] تعریف شده است. فهرست اینترسپتورهای تعریف‌شده در آنجا به شرح زیر است:


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

در میان این میان‌گیرها، یکی مسئول تزریق مقادیر valeuri پارامترهای parami، که در قالب parami=valeuri همراه درخواست هستند، به اقدام است. مشخص است که valeuri در صورت وجود، از طریق متد setParami به فیلد parami اکشن تزریق خواهد شد. در غیر این صورت، هیچ تزریقی انجام نمی‌شود و هیچ خطایی گزارش نمی‌گردد.

رشته parami=valeuri یک رشته کاراکتری است. تاکنون، valeuri به فیلدهای parami از نوع String تزریق شده است:

private String parami ;

تزریق رشته valeuri به عنوان مقدار رشته parami هیچ مشکلی ایجاد نکرد. اگر parami از نوع String نباشد، آنگاه valeuri باید از parami به نوع Ti تبدیل شود. این مشکل تبدیل است. برای مثال، ممکن است بخواهیم سن یک عدد صحیح باشد و در اکشن عبارت زیر را بنویسیم:

private int age ;

علاوه بر این، ممکن است بخواهیم سن را بین ۱ تا ۱۵۰ محدود کنیم. این یک مسئله اعتبارسنجی است. پارامتر parami ممکن است بدون لزوماً معتبر بودن به نوع صحیح تبدیل شود. بنابراین دو مرحله باید انجام شود. بازگشت به نمودار جریان برای پردازش یک درخواست:

دو اینترسپتر به ترتیب مسئول تبدیل و اعتبارسنجی پارامترها هستند. اگر هر یک از این مراحل با شکست مواجه شود، درخواست به اکشن بعدی (مسیر قرمز رنگ بالا) ارسال نخواهد شد. فرم ارسال‌شده با پارامترهای نادرست، همراه با پیام‌های خطا مجدداً نمایش داده می‌شود.

مداخلهگرهای مربوط به تبدیل و اعتبارسنجی پارامترها به ترتیب conversionError و validation هستند که در خطوط ۱۹ و ۲۰ لیست مداخلهگرهای نشان داده شده در بالا فهرست شده‌اند. توجه کنید که در خطوط ۲۰ تا ۲۲، اگر متد فراخوانی‌شده یکی از متدهای input، back، cancel باشد، اینترسپتور validation اعمال نمی‌شود، browse. ما بعداً از این ویژگی استفاده خواهیم کرد.

ما کار را با بررسی تبدیل و اعتبارسنجی اعداد صحیح آغاز می‌کنیم. ما زمان زیادی را صرف این مثال اول خواهیم کرد، زیرا اعتبارسنجی شامل عناصر متعددی است. پس از پوشش این موارد، به سرعت به مثال‌های بعدی می‌رویم.

11.1. فرم

  • به [1]، فرم ورود داده
  • به [2]، نتیجه ارسال بدون وارد کردن هیچ مقداری

11.2. پروژه NetBeans

پروژه NetBeans به شرح زیر است:

  • در [1]، سه نمای برنامه
  • در [2]، کد منبع، فایل‌های پیام بین‌المللی‌سازی‌شده و فایل‌های پیکربندی Struts.

11.3. پیکربندی Struts

برنامه از طریق فایل‌های [struts.xml] و [example.xml] پیکربندی می‌شود.

فایل [struts.xml] به شرح زیر است:


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

خطوط ۱۲–۱۸ اکشن [/example/Accueil] را به‌عنوان اکشن پیش‌فرض زمانی که کاربر اکشنی را مشخص نمی‌کند، تعریف می‌کنند.

فایل [example.xml] به شرح زیر است:


<?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>
  • خطوط ۸–۱۰: عمل [Accueil] نما [Accueil.JSP] را نمایش می‌دهد
  • خط ۱۱: عمل [FormInt] باعث اجرای روش execute از کلاس [example.FormInt] به‌صورت پیش‌فرض می‌شود. خواهیم دید که دو متد دیگر اجرا خواهند شد: متدهای input و cancel. این متدها سپس در پارامترهای پرس‌وجو مشخص خواهند شد.
  • خط ۱۲: کلید input نما [FormInt.JSP] (خط ۵) را نمایش خواهد داد. این نما مربوط به فرم است.
  • خط ۱۳: کلید cancel توسط متد cancel که با لینک [Annuler] مرتبط است، بازگردانده می‌شود. نما بازگردانده‌شده سپس نمای [Accueil.JSP] پس از یک هدایت (type=redirect) خواهد بود.
  • خط ۱۴: کلید success توسط متد execute از اکشن [FormInt] بازگردانده می‌شود. اگر درخواست به متد execute برسد، به این معنی است که با موفقیت از تمام interceptorها عبور کرده است، به‌ویژه آنهایی که اعتبار پارامترها را بررسی می‌کنند. سپس متد execute به سادگی کلید success را بازمی‌گرداند که نمای تأیید [ConfirmationInt.JSP] را نمایش می‌دهد.

11.4. فایل‌های پیام

فایل [messages.properties] به شرح زیر است:


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

علاوه بر این فایل، ویوها از فایل زیر استفاده می‌کنند: [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]

فایل [FormInt.properties] تنها زمانی استفاده می‌شود که اکشنی که نما را تولید کرده، اکشن [FormInt] باشد. این روشی برای تقسیم فایل پیام در صورت بزرگ بودن آن است. پیام‌های مربوط به اقدام Action در فایل [Action.properties] قرار دارند.

11.5. نماها و اقدامات

اکنون نماها و اقدامات برنامه را تشریح خواهیم کرد. بر اساس پیکربندی برنامه:


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

می‌توانیم ببینیم که سه نما ([Accueil.JSP, FormInt.JSP, ConfirmationFormInt.JSP]) و دو اقدام ([Accueil, FormInt]) وجود دارد.

11.5.1. Accueil.JSP

نما [Accueil.JSP] به شرح زیر است:

 

کد آن به شرح زیر است:


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

لینک در خط 14 کد 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>

بنابراین این یک لینک به اقدام [FormInt] است که در [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>

بنابراین، کلیک بر روی این لینک، کلاس [example.FormInt] را ایجاد کرده و متد input آن را اجرا خواهد کرد. از آنجایی که این متد وجود ندارد، متد input از کلاس والد ActionSupport اجرا خواهد شد. این متد کاری جز بازگرداندن کلید input انجام نمی‌دهد. بنابراین، ویوی [/example/FormInt.JSP] نمایش داده خواهد شد.

علاوه بر این، متد input یکی از متدهایی است که توسط interceptor اعتبارسنجی نادیده گرفته می‌شود:


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

در نتیجه، هیچ اعتبارسنجی پارامتر انجام نخواهد شد. این مهم است زیرا در اینجا هیچ پارامتری وجود ندارد و بعداً خواهیم دید که قواعد اعتبارسنجی به حضور شش پارامتر نیاز دارند.

11.5.2. اقدام [FormInt]

اقدام [FormInt] با کلاس زیر [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 {

  // سازنده بدون آرگومان
  public FormInt() {
  }

  //مدل عملیاتی
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormIntModel());
    }
    return session.get("model");
  }

  public String cancel() {
    //پاکسازی مدل
    ((FormIntModel) getModel()).clearModel();
    // نتیجه
    return "cancel";
  }

  @SkipValidation
  public String clearModel() {
    // بازنشانی مدل
    ((FormIntModel) getModel()).clearModel();
    // نتیجه
     return INPUT;
  }
  
  //SessionAware
  private Map<String, Object> session;

  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  //اعتبارسنجی
  @Override
  public void validate() {
    //آیا ورودی int6 معتبر است؟
    if (getFieldErrors().get("int6") == null) {
      int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
      if (int6 < 2 || int6 > 20) {
        addFieldError("int6", getText("int6.error"));
      }
    }
  }
}

ما هر زمان که لازم باشد، این کد را توضیح خواهیم داد. فعلاً:

  • خط ۹، کلاس [FormInt] دو رابط را پیاده‌سازی می‌کند:
    • ModelDriven، که تنها یک متد دارد، getModel در خط 16
    • SessionAware، که تنها یک متد دارد، setSession در خط ۴۱
  • خطوط ۱۶–۲۱: پیاده‌سازی رابط ModelDriven. شایان ذکر است که این رابط امکان واگذاری مدل یک نما به یک کلاس خارجی را فراهم می‌کند، در این مورد کلاس زیر [FormIntModel]:

package example;

public class FormIntModel {

  // سازنده بدون پارامتر
  public FormIntModel() {
  }

  // fields of the form
  private String int1;
  private Integer int2;
  private Integer int3;
  private Integer int4;
  private Integer int5;
  private String int6;

  // قالب خالی
  public void clearModel(){
    int1=null;
    int2=null;
    int3=null;
    int4=null;
    int5=null;
    int6=null;
  }

  // گیرنده و تنظیم‌کننده
   ...
}

مدل [FormIntModel] دارای شش فیلد است که با شش فیلد ورودی در نمای [FormInt.JSP] مطابقت دارند. این شش فیلد مقادیر ارسال‌شده را دریافت خواهند کرد. چهار مورد از آن‌ها از نوع Integer هستند. بنابراین، مسئله تبدیل از String به Integer برای این فیلدها پیش خواهد آمد. متد clearModel امکان ریست شدن مدل را فراهم می‌کند.

بیایید به روش getModel از اقدام [FormInt] بازگردیم:


  // مدل اقدام
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormIntModel());
    }
    return session.get("model");
}
  • خطوط ۳–۵: مدل در جلسه جستجو می‌شود. اگر وجود نداشته باشد، یک نمونه از مدل ایجاد شده و در جلسه قرار می‌گیرد.
  • خط ۶: در حالی که برای هر درخواست جدید به اکشن یک نمونه ایجاد می‌شود، مدل آن در جلسه باقی می‌ماند.

می‌توانیم ببینیم که کلاس، متدی به نام input تعریف نکرده است، اما کلاس والد یکی دارد که کلید input را برمی‌گرداند. اجرای این متد منجر به نمایش ویوی [FormInt.JSP] می‌شود که اکنون آن را ارائه خواهیم داد.

11.5.3. ویوی [FormInt.JSP]

نما [FormInt.JSP] به شرح زیر است:

  • در [1]، فرم خالی
  • در [2]، فرم پس از اعتبارسنجی پارامترهای نادرست.

کد آن به شرح زیر است:


<%@ 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>
  • خطوط ۱۲–۱۷: شش فیلد ورودی متناظر با شش فیلد در قالب [FormIntModel] برای اقدام [FormInt]. وقتی نما نمایش داده می‌شود، از ویژگی‌های value فیلدهای ورودی برای تعیین مقادیر نمایش‌داده‌شده در این فیلدها استفاده می‌شود. اگر ویژگی value وجود نداشته باشد، از ویژگی name استفاده می‌شود.
  • خط ۱۲: فیلد ورودی با فیلد int1 از اکشن یا قالب آن مرتبط می‌شود (name) اگر اکشن رابط ModelDriven را پیاده‌سازی کند. در اینجا نیز همین امر صادق است. همین موضوع برای سایر فیلدها نیز صدق می‌کند.
  • خط ۱۸: دکمه [Valider] ورودی‌ها را به اکشن [FormInt] که در خط ۱۱ تعریف شده ارسال می‌کند. متد آن execute اجرا خواهد شد.
  • خطوط 21–22: لینک [Annuler] متد [FormInt.cancel] را اجرا می‌کند.
  • خطوط ۲۴–۲۵: لینک [Raz modèle] متد [FormInt.clearModel] را اجرا می‌کند.

11.5.4. نما [ConfirmationFormInt.JSP]

این زمانی نمایش داده می‌شود که همه ورودی‌های فرم [FormInt.JSP] معتبر باشند.

  • در [1]، مقادیر معتبر ارسال می‌شوند
  • در [2]، صفحه تأیید

کد مربوط به نما [ConfirmationInt.JSP] به شرح زیر است:


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

برای درک این کد، مهم است به خاطر داشته باشید که نما پس از instantiate شدن کلاس [FormInt] نمایش داده می‌شود. بنابراین فیلدهای این کلاس و مدل آن، [FormIntModel]، برای نما قابل دسترسی هستند.

  • خطوط ۱۶–۳۸: مقادیر شش فیلد نمایش داده می‌شوند
  • خطوط ۴۲–۴۳: یک لینک به اکشن [FormInt]. کد تولیدشده برای این لینک به شرح زیر است:

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

URL خاص در لینک نشان می‌دهد که متد input از اکشن [FormInt] باید درخواست را پردازش کند. بیایید پیکربندی اقدام [FormInt] را در [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>

متد input از کلاس [FormInt] همان متد کلاس والد آن ActionSupport خواهد بود. روش input کلاس [FormInt] پس از اجرای interceptors اجرا می‌شود

ما می‌دانیم که فراخوانی متد input توسط interceptor اعتبارسنجی نادیده گرفته می‌شود. بنابراین هیچ اعتبارسنجی انجام نخواهد شد.

نما [FormInt.JSP] نمایش داده می‌شود:

در [2]، فیلدهای ورودی به مقادیر اولیه خود بازمی‌گردند. این ممکن است طبیعی به نظر برسد، اما این‌طور نیست. با فراخوانی اکشن [FormInt]، کلاس مرتبط [FormInt] نمونه سازی شد. از آنجا که این کلاس رابط ModelDriven را پیاده‌سازی می‌کند، متد آن getModel فراخوانی شد:


  // مدل عملی
  public Object getModel() {
    if (session.get("model") == null) {
      session.put("model", new FormIntModel());
    }
    return session.get("model");
}

می‌توانیم ببینیم که مدل اقدام از جلسه بازیابی شده است. در مرحله قبلی، این مدل با مقادیر ارسال‌شده به‌روزرسانی شده بود. بنابراین دوباره این مقادیر را مشاهده می‌کنیم. اگر مدل را در جلسه قرار نداده بودیم، در نما [FormInt.JSP] شش فیلد خالی داشتیم.

11.5.5. اقدام [FormInt!clearModel]

اقدام [Formint!clearModel] با کلیک روی پیوند [Raz modèle] فعال می‌شود:

  • به [1]، فرم پس از وارد کردن نادرست
  • به [2]، فرم پس از کلیک روی لینک [Raz modèle].

روش [FormInt.clearModel] به شرح زیر است:


  @SkipValidation
  public String clearModel() {
    // بازنشانی مدل
    ((FormIntModel) getModel()).clearModel();
    // نتیجه
    return INPUT;
}
  • خط ۱: هیچ اعتبارسنجی‌ای برای انجام وجود ندارد. ما از نماد @SkipValidation برای نشان دادن این موضوع استفاده می‌کنیم. بنابراین، interceptor اعتبارسنجی هیچ اعتبارسنجی‌ای انجام نخواهد داد.
  • خط ۴: متد [FormIntModel].clearModel اجرا می‌شود. ما قبلاً با این مورد مواجه شده‌ایم. این متد شش فیلد مدل را به null ریست می‌کند.
  • خط ۷: متد کلید input را بازمی‌گرداند.

بازگشت به پیکربندی اقدام [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>

می‌توانیم ببینیم که کلید input نما [FormInt.JSP] را نمایش خواهد داد. این نما شش فیلد مدل را نمایش می‌دهد. از آنجا که این‌ها روی null تنظیم شده‌اند، نما شش فیلد خالی ([2]) را نمایش می‌دهد.

11.5.6. اقدام [FormInt!cancel]

عمل [Formint!cancel] با کلیک روی لینک [Annuler]:

  • به [1]، فرم پس از وارد کردن نادرست
  • به [2]، صفحهٔ اصلی پس از کلیک روی لینک [Annuler].

روش [FormInt.cancel] به شرح زیر است:


  public String cancel() {
    //پاکسازی مدل
    ((FormIntModel) getModel()).clearModel();
    // نتیجه
    return "cancel";
}
  • خط ۱: توجه کنید که این متد با حاشیه‌نویسی SkipValidation همراه نیست. با این حال، ما نمی‌خواهیم اعتبارسنجی‌ها را انجام دهیم. متد cancel یکی از چهار متد input است، back، cancel و browse که توسط interceptor اعتبارسنجی نادیده گرفته می‌شوند؛ بنابراین، annotation SkipValidation مورد نیاز نیست.
  • خط ۳: این قالب را پاک می‌کند
  • خط ۵: کلید را روی cancel تنظیم می‌کند

بازگشت به پیکربندی اکشن [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>

می‌توانیم ببینیم که کلید cancel پس از هدایت مشتری، نما [Accueil.JSP] را نمایش خواهد داد. این توسط نما [2] نشان داده شده است.

11.6. فرآیند اعتبارسنجی

اکنون به اعتبارسنجی شش فیلد ورودی مرتبط با شش فیلد زیر در مدل می‌پردازیم:


  // fields of the form
  private String int1;
  private Integer int2;
  private Integer int3;
  private Integer int4;
  private Integer int5;
private String int6;

این اعتبارسنجی هر بار که کلاس [FormInt] نمونه سازی می‌شود و متدی که در حال اجراست توسط مداخله گر اعتبارسنجی نادیده گرفته نمی‌شود، انجام می‌گیرد. این فرآیند توسط موارد زیر کنترل می‌شود:

  • فایل [FormInt-validation.xml]، اگر در همان پوشه کلاس [FormInt] وجود داشته باشد
  • متد [FormInt.validate]، در صورت وجود.
  • در [1]: فایل [xwork-validator-1.0.2.dtd] مورد نیاز برای فرآیند اعتبارسنجی
  • به [2]: فایل [FormInt-validation.xml] در همان پوشه با کلاس [FormInt]

فایل [FormInt-validation.xml] به شرح زیر است:


<!--
<!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/example-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>
  • در [3]، URL از DTD (تعریف نوع سند) فایل اعتبارسنجی. این باید قابل دسترسی باشد؛ در غیر این صورت، فایل اعتبارسنجی استفاده نخواهد شد.
  • در [7]، URL از DTD که توسط برنامه استفاده می‌شود. ما فایل DTD را در پوشه [example] پروژه exemple-09 [1] قرار داده‌ایم تا حتی در صورت عدم دسترسی به اینترنت نیز در دسترس باشد.
  • خطوط ۱۱–۲۰: شرایط اعتبارسنجی پارامتر int1 مرتبط با فیلد int1 در قالب را تعریف می‌کنند.

برچسبی به نام int1 در فرم به صورت زیر است:


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

میدان int1 در قالب به صورت زیر اعلام شده است:


private String int1;
  • خطوط ۱۲–۱۴: بررسی کنید که پارامتر int1 وجود دارد (نه null) و طول آن غیرصفر است. اگر اینطور نباشد، یک پیام خطا با فیلد ورودی مرتبط می‌شود. این پارامتر در [FormInt.properties] به صورت زیر تعریف شده است:

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

اگر خطایی رخ دهد، فرآیند اعتبارسنجی پارامتر int1 متوقف می‌شود (short-circuit=true).

  • خطوط ۱۵–۱۹: اعتبار پارامتر int1 با استفاده از عبارت منظم بررسی می‌شود.
  • خط ۱۶: عبارت منظم، در این مورد دو رقم بدون هیچ‌گونه پیش‌وند یا پس‌وند.
  • خط ۱۷: قبل از مقایسه با عبارت منظم، هرگونه فاصلهٔ اضافی در ابتدای یا انتهای پارامتر int1 حذف می‌شود.
  • خط ۱۸: هر پیام خطا. این همانند اعتبارسنج قبلی است.

بیایید ببینیم چگونه کار می‌کند:

  • به [1] تبدیل می‌شود، که ورودی نادرستی برای فیلد int1 است.
  • برای [2]، صفحه‌ای که بازگردانده شد:
    • پیام خطا برای کلید int1.error نمایش داده می‌شود. این پیام به رنگ قرمز است.
    • برچسب فیلد نادرست نیز به رنگ قرمز است.
    • ورودی نادرست دوباره نمایش داده می‌شود. این باید پیش‌بینی شود زیرا لزوماً رفتار پیش‌فرض نیست.

ما دیده‌ایم که اعتبارسنجی فرم، اجرای متد [FormInt].execute را در صورتی که درخواست موفق شود از تمام interceptorها، به‌ویژه interceptor اعتبارسنجی، عبور کند، تحریک می‌کند:

  • اگر درخواست به متد execute اکشن برسد، همان‌طور که دیدیم، این متد کلید success را به کنترلر بازمی‌گرداند.
  • اگر interceptor اعتبارسنجی درخواست را متوقف کند زیرا پارامترهای در حال بررسی نامعتبر هستند، آنگاه کلید input به کنترلر بازگردانده می‌شود.

از آنجایی که اکشن [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>

هنگامی که یک خطای اعتبارسنجی رخ می‌دهد، نمای [FormInt.JSP] نمایش داده می‌شود، یعنی فرم. تگ‌های Struts برای نمایش هرگونه پیام خطا که به آن‌ها مرتبط است طراحی شده‌اند. بنابراین ما ویوی [FormInt.JSP] را با پیام‌های خطا که به فیلدهای مختلف متصل شده‌اند، مشاهده خواهیم کرد. این موضوع در ویوی [2] نشان داده شده است.

اکنون بیایید اعتبارسنجی فیلد int2 را بررسی کنیم که در مدل به شرح زیر تعریف شده است:


private Integer int2;

اعتبارسنجی برای فیلد int2 در [FormInt-validation.xml] به شرح زیر است:


<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>
  • خطوط ۲–۴: بررسی کنید که پارامتر int2 وجود دارد.
  • خطوط ۵–۷: بررسی کنید که تبدیل رشته به عدد صحیح امکان‌پذیر است
  • خطوط ۳ و ۶: پیام خطا برای کلید int2.error به شرح زیر است:

int2.error=Tapez un nombre entier

اعتبارسنجی فیلدهای Integer و int3 در مدل درون [FormInt-validation.xml] به شرح زیر است:


<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>
  • خطوط ۸–۱۱: بررسی کنید که فیلد int3 از نوع عدد صحیح ≥ -1 باشد
  • خطوط ۳ و ۷: پیام خطا برای کلید int3.error به شرح زیر است:

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

اعتبارسنجی فیلدهای Integer و int4 در قالب درون [FormInt-validation.xml] به شرح زیر است:


<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>
  • خطوط ۸–۱۱: بررسی کنید که یک عدد صحیح کوچکتر یا مساوی ۱۰ است
  • خطوط ۳ و ۷: پیام خطا برای کلید int4.error به شرح زیر است:

int4.error=Tapez un nombre entier <=10

اعتبارسنجی برای فیلدهای Integer و int5 در مدل در [FormInt-validation.xml] به شرح زیر است:


<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>
  • خطوط ۵–۹: بررسی کنید که یک عدد صحیح در بازه [1, 10] باشد.
  • خطوط ۳ و ۸: پیام خطا برای کلید int5.error به شرح زیر است:

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

اعتبارسنجی برای فیلد String int6 در قالب در [FormInt-validation.xml] به شرح زیر است:


<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>
  • خطوط ۵–۹: بررسی کنید که int6 یک رشتهٔ دو رقمی است.
  • خط ۳، ۸: پیام خطا برای کلید int6.error به شرح زیر است:

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

اعتبارسنجی قبلی بررسی نمی‌کند که پارامتر int6 یک عدد صحیح در بازه [2,20] باشد. این بررسی در متد [FormInt].validate انجام می‌شود که پس از پردازش فایل [FormInt-validation.xml] اجرا می‌گردد. این متد به شرح زیر است:


  //اعتبارسنجی
  @Override
  public void validate() {
    //آیا ورودی int6 معتبر است؟
    if (getFieldErrors().get("int6") == null) {
      int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
      if (int6 < 2 || int6 > 20) {
        addFieldError("int6", getText("int6.error"));
      }
    }
}
  • خط ۵: سیستم بررسی می‌کند که آیا خطایی مرتبط با فیلد int6 وجود دارد یا خیر. در صورت وجود، فرآیند ادامه نمی‌یابد.
  • خط ۶: اگر هیچ خطایی وجود نداشته باشد، فیلد String int6 از مدل بازیابی شده و به یک عدد صحیح تبدیل می‌شود.
  • خط ۷: بررسی می‌کنیم که عدد صحیح بازیابی‌شده در محدوده [2,20] قرار دارد.
  • خط ۸: اگر اینطور نباشد، یک پیام خطا به فیلد int6 متصل می‌شود. این پیام خطا با استفاده از کلید int6.error در فایل پیام‌ها جستجو می‌شود.

اگر در پایان این فرآیند اعتبارسنجی، خطایی وجود داشته باشد، فراخوانی متد [FormInt].execute متوقف شده و کلید input به کنترلر Struts بازگردانده می‌شود.

11.7. جزئیات نهایی

ما چندین روش برای وارد کردن اعداد صحیح دیده‌ایم. همه‌ی آن‌ها معادل نیستند. برای مثال، فیلدهای ورودی int5 و int6 را در نظر بگیرید:

در ویو [FormInt.JSP]، آن‌ها به شرح زیر تعریف شده‌اند:


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

قالب آن‌ها در [FormIntModel.java] تعریف شده است:


  private Integer int5;
private String int6;

میدان int5 از نوع Integer است، در حالی که میدان int6 از نوع String است. قواعد اعتبارسنجی آن‌ها متفاوت است:


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

اعتبارسنجی فیلد int6 با استفاده از روش validate از اقدام [FormInt] انجام می‌شود:


  public void validate() {
    //آیا ورودی int6 معتبر است؟
    if (getFieldErrors().get("int6") == null) {
      int int6 = Integer.parseInt(((FormIntModel) getModel()).getInt6());
      if (int6 < 2 || int6 > 20) {
        addFieldError("int6", getText("int6.error"));
      }
}

اگرچه قواعد اعتبارسنجی به شیوه‌های متفاوتی بیان شده‌اند، هر دو هدفشان تأیید این است که مقدار واردشده در فیلد، یک عدد صحیح در بازهٔ مشخصی باشد. با این حال، رفتار فیلدهای int5 و int6 در زمان اجرا متفاوت است، همان‌طور که در اسکرین‌شات‌های زیر نشان داده شده است:

  • در [1]، همان ورودی نادرست برای هر دو فیلد
  • در [2]، صفحه خطا بازگردانده می‌شود. این دو فیلد پیام‌های خطای متفاوتی نمایش می‌دهند.
  • در [3]، پیامی ناخواسته برای فیلد int5 نمایش داده می‌شود زیرا به زبان انگلیسی است. این به دلیل شکست تبدیل رشته به عدد صحیح است. همچنین یک استثنا در لاگ‌های آپاچی وجود دارد:
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;)]

جالب اینجاست که Struts به دنبال متدی به نام FormIntModel.setInt5(String value) گشت اما نتوانست آن را پیدا کند.

کلید پیام ناخواسته xwork.default.invalid.fieldvalue است. برای ترجمه آن به فرانسوی، کافی است یک متن فرانسوی را به این کلید اختصاص دهید. بنابراین خط زیر را به فایل [messages.properties] اضافه می‌کنیم:


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

11.8. Conclusion

این پایان بررسی ما از این اولین کاربرد اختصاص‌یافته به اعتبارسنجی پارامتر است. توضیح این موضوع پیچیده بود. اکنون به کاربردهای مشابه خواهیم پرداخت. بنابراین، تنها تغییرات را توضیح خواهیم داد.