日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

JSF 轉(zhuǎn)換與驗(yàn)證

系統(tǒng) 1840 0
在本文中,我們將介紹 JSF 轉(zhuǎn)換和驗(yàn)證框架的概念,它比您所想的要容易使用得多,也靈活得多。

首先我們將介紹應(yīng)用于 JSF 生命周期的轉(zhuǎn)換和驗(yàn)證過程,然后展示一個(gè)簡單的 JSF 應(yīng)用程序中的默認(rèn)轉(zhuǎn)換和驗(yàn)證過程。接著將展示如何創(chuàng)建和插入自定義的實(shí)現(xiàn),以應(yīng)對要求更高的場景。正如 Rick 在以前的文章中所說的,我們會(huì)理論與實(shí)踐并重,先介紹概念,再用一個(gè)實(shí)際例子說明這些概念的應(yīng)用。示例應(yīng)用程序?qū)⒑w大多數(shù)轉(zhuǎn)換和驗(yàn)證用例,雖然只是初級的。

注意,示例應(yīng)用程序的默認(rèn)編譯環(huán)境是 Maven,不過, 還提供了一個(gè) Ant 腳本。可以單擊本頁頂部或者底部的 Code 圖標(biāo)下載示例源代碼。為了簡便起見,您會(huì)發(fā)現(xiàn),該例子的設(shè)置與上一篇文章中的一樣。關(guān)于構(gòu)建環(huán)境配置的更多說明,包括在 Ant 環(huán)境中而不是在 Maven 環(huán)境中編譯和運(yùn)行示例應(yīng)用程序的說明,請參閱 參考資料

轉(zhuǎn)換和驗(yàn)證
雖然在 JSF Web 應(yīng)用程序中使用轉(zhuǎn)換和驗(yàn)證不一定要理解 JavaServer Faces 生命周期的基礎(chǔ)知識(shí),但是在深入轉(zhuǎn)換和驗(yàn)證內(nèi)容之前,最好對一些基本知識(shí)做一回顧。此外,掌握一點(diǎn) JSF 生命周期技巧可以極大地幫助簡化 Web 應(yīng)用程序的開發(fā)工作。還有助于更好地理解 JSF 的可插入能力。

圖 1 描繪了我們所說的“基本 JSF 生命周期”。 基本 是在暗示這只是一個(gè)典型的處理所提交表單值的請求-響應(yīng)(request-and-response)場景。

圖 1. 基本 JSF 生命周期
JSF 基本生命周期圖

顯然,不同的場景對這里重點(diǎn)描述的生命周期有不同的影響。我們將在本文稍后介紹其中一些場景。現(xiàn)在,只需要注意轉(zhuǎn)換和驗(yàn)證過程發(fā)生在 應(yīng)用請求值 處理驗(yàn)證 呈現(xiàn)響應(yīng) 階段即可。

我們將在稍后介紹為什么轉(zhuǎn)換和驗(yàn)證會(huì)在這些階段出現(xiàn),但是首先讓我們澄清一個(gè)更基本的問題: 轉(zhuǎn)換 是什么?簡單地說,轉(zhuǎn)換是確保數(shù)據(jù)擁有正確的對象或者類型的過程。下面是兩個(gè)典型的轉(zhuǎn)換:

  • 字符串值可以轉(zhuǎn)換為 java.util.Date
  • 字符串值可以轉(zhuǎn)換為 Float。

至于 驗(yàn)證 ,它用于確保數(shù)據(jù)包含所期望的內(nèi)容。下面是兩個(gè)典型的驗(yàn)證:

  • java.util.Date 的格式為 MM/yyyy。
  • Float 在 1.0 和 100.0 之間。

關(guān)注生命周期階段
轉(zhuǎn)換和驗(yàn)證的主要目的是確保在更新模型數(shù)據(jù)之前已經(jīng)經(jīng)過了正確的 無害處理 。之后,當(dāng)需要調(diào)用應(yīng)用程序方法用這些些數(shù)據(jù)實(shí)際 做一些事情 時(shí),就可以有把握地假定模型的某些狀態(tài)。轉(zhuǎn)換和驗(yàn)證使您可以側(cè)重于業(yè)務(wù)邏輯,而不是側(cè)重于對輸入數(shù)據(jù)進(jìn)行繁瑣的資格認(rèn)定,比如 null 檢驗(yàn)、長度限定、范圍邊界,等等。

因此,在 更新模型數(shù)據(jù) 生命周期階段中,在組件數(shù)據(jù)被綁定到 backing bean 模型 之前 進(jìn)行轉(zhuǎn)換和驗(yàn)證處理是有道理的。正如圖 1 所示,轉(zhuǎn)換發(fā)生在應(yīng)用請求值階段,而驗(yàn)證發(fā)生在處理驗(yàn)證階段。圖 2 突出顯示了這些階段。

圖 2. 要關(guān)注的轉(zhuǎn)換和驗(yàn)證階段
轉(zhuǎn)換和驗(yàn)證階段的 JSF 生命周期圖

關(guān)于 immediate 屬性
注意,圖 2 中描繪的轉(zhuǎn)換和驗(yàn)證過程表示了將 UIInput 組件的 immediate 屬性設(shè)置為 false 時(shí)的應(yīng)用程序流程。如果這個(gè)屬性設(shè)置為 true ,那么轉(zhuǎn)換和驗(yàn)證會(huì)發(fā)生在生命周期更早的時(shí)期,即應(yīng)用請求值階段(參見圖 3)。對使用 immediate 屬性的詳細(xì)討論超出了本文的范圍,但是在某些情況下,比如管理動(dòng)態(tài)清單(可能您還記得,本系列的上一篇文章中曾介紹過),它很有用,它甚至可以繞過驗(yàn)證(在與 UICommand 組件結(jié)合使用時(shí))。能想像一個(gè)需要完全繞過驗(yàn)證的應(yīng)用程序嗎?

圖 3 展示了當(dāng) immediate 屬性設(shè)置為 true 時(shí),在 JSF 應(yīng)用程序生命周期中的哪些地方進(jìn)行轉(zhuǎn)換和驗(yàn)證。

圖 3. 將 immediate 屬性設(shè)置為 true
將 immediate 屬性設(shè)置為 true 時(shí)的 JSF 生命周期圖

實(shí)際的例子
下面,我們將用一個(gè)示例應(yīng)用程序展示所討論的概念。本月的示例應(yīng)用程序?qū)⒄故?JSF 的轉(zhuǎn)換和驗(yàn)證能力。記住,這個(gè)示例應(yīng)用程序非常簡單,沒有追求一些不必要的面面俱到:無論如何,我們的目的不是構(gòu)建一個(gè)在真實(shí)世界中使用的應(yīng)用程序!這個(gè)示例應(yīng)用程序?qū)⒄故疽韵聨c(diǎn):

  • 使用標(biāo)準(zhǔn) JSF 轉(zhuǎn)換器轉(zhuǎn)換表單字段數(shù)據(jù)。
  • 使用標(biāo)準(zhǔn) JSF 驗(yàn)證組件驗(yàn)證表單字段數(shù)據(jù)。
  • 如何編寫自定義轉(zhuǎn)換器和驗(yàn)證器。
  • 如何在 faces-config.xml 文件中注冊自定義轉(zhuǎn)換器和驗(yàn)證器。
  • 如何定制默認(rèn)錯(cuò)誤消息。

這個(gè)示例應(yīng)用程序是一個(gè)簡單的用戶注冊表單。我們的目標(biāo)是收集用戶數(shù)據(jù),比如姓名、年齡、電子郵箱地址和電話號碼。然后,我們將展示如何利用 JSF 轉(zhuǎn)換和驗(yàn)證確保收集的數(shù)據(jù)對于模型是適合的。

這個(gè)應(yīng)用程序使用了三個(gè) JSP 頁:

  • index.jsp 將用戶定向到 UserRegistration.jsp。
  • UserRegistration.jsp 包含應(yīng)用程序的表單字段。
  • results.jsp 通知應(yīng)用程序用戶已經(jīng)注冊。

我們將首先分析編寫 JSF 轉(zhuǎn)換過程的選擇。

JSF 轉(zhuǎn)換
如前所述,轉(zhuǎn)換是確保數(shù)據(jù)對象或者類型正確的一個(gè)過程,因此,我們將字符串值轉(zhuǎn)換為其他類型,比如 Date 對象、基本浮點(diǎn)型或者 Float 對象。可以使用自帶的轉(zhuǎn)換器,也可以編寫自定義的轉(zhuǎn)換器。

JSF 提供了許多標(biāo)準(zhǔn)數(shù)據(jù)轉(zhuǎn)換器。也可以通過實(shí)現(xiàn) Converter 接口插入自定義轉(zhuǎn)換器,但是這些將在后面進(jìn)行介紹。下表顯示了 JSF 進(jìn)行簡單數(shù)據(jù)轉(zhuǎn)換所使用的轉(zhuǎn)換器 id 及其對應(yīng)的實(shí)現(xiàn)類。大多數(shù)數(shù)據(jù)轉(zhuǎn)換是自動(dòng)發(fā)生的。

javax.faces.BigDecimal javax.faces.convert.BigDecimalConverter
javax.faces.BigInteger javax.faces.convert.BigIntegerConverter
javax.faces.Boolean javax.faces.convert.BooleanConverter
javax.faces.Byte javax.faces.convert.ByteConverter
javax.faces.Character javax.faces.convert.CharacterConverter
javax.faces.DateTime javax.faces.convert.DateTimeConverter
javax.faces.Double javax.faces.convert.DoubleConverter
javax.faces.Float javax.faces.convert.FloatConverter

圖 4 展示了用戶年齡的默認(rèn)轉(zhuǎn)換。JSF 標(biāo)簽配置如下:

            
              
    <!-- UserRegistration.jsp -->
    <h:inputText id="age" value="#{UserRegistration.user.age}"/>

            
          

圖 4. 用戶注冊:年齡的默認(rèn)轉(zhuǎn)換
默認(rèn)轉(zhuǎn)換

各種情況的轉(zhuǎn)換器
UserRegistration.user.age 表示一個(gè)值綁定屬性,它的類型為 int 。對于基本型或者 BigInteger / BigDecimal 的綁定,JSF 選擇了標(biāo)準(zhǔn)轉(zhuǎn)換器。不過,還可以通過 <f:converter/> 標(biāo)簽,利用一個(gè)特定的轉(zhuǎn)換器來增加粒度,如下所示。

            
              
<!-- UserRegistration.jsp -->
<h:inputText id="age" value="#{UserRegistration.user.age}">
             <f:converter id="javax.faces.Short"/>
</h:inputText>

            
          

在圖 5 中,可以看到 JSF 使用標(biāo)準(zhǔn)轉(zhuǎn)換器的場景。在這種情況下,雖然年齡實(shí)際上是一個(gè)有效的整數(shù),但轉(zhuǎn)換仍然會(huì)失敗,因?yàn)樵撝挡皇嵌陶偷摹?

圖 5. 使用 f:converter 標(biāo)簽
使用 f:converter 標(biāo)簽

選擇日期格式樣式
盡管在默認(rèn)情況下,JSF 可以很好地處理基本型及類似的類型,但是在處理日期數(shù)據(jù)時(shí),必須指定轉(zhuǎn)換標(biāo)簽 <f:convertDateTime/> 。這個(gè)標(biāo)簽基于 java.text 包,并使用短、長和自定義樣式。下面是一個(gè)例子:

            
              
<!-- UserRegistration.jsp -->
<h:inputText id="birthDate" value="#{UserRegistration.user.birthDate}">
             <f:convertDateTime pattern="MM/yyyy"/>
</h:inputText>

            
          

這個(gè)例子展示了如何用 <f:convertDateTime/> 確保用戶的生日可以轉(zhuǎn)換為格式為 MM/yyyy(月/年)的日期對象。請參閱 JSF 的 java.text.SimpleDataFormat (在 參考資料 中),以獲取模式列表。

其他樣式
除了可以轉(zhuǎn)換日期和時(shí)間格式外,JSF 還提供了處理像百分?jǐn)?shù)或者貨幣數(shù)據(jù)這類值的特殊轉(zhuǎn)換器。這個(gè)轉(zhuǎn)換器處理分組(如逗號)、小數(shù)、貨幣符號等。例如,以下 <f:convertNumber/> 的用法就是處理貨幣的一種技巧:

            
              
<!-- UserRegistration.jsp -->
<h:inputText id="salary" value="#{UserRegistration.user.salary}">
             <f:convertNumber maxFractionDigits="2"
                       groupingUsed="true"
                       currencySymbol="$"
                       maxIntegerDigits="7"
                       type="currency"/>
</h:inputText>

            
          

在圖 6 中,可以看到一些格式編排不正確的貨幣數(shù)據(jù),以及所導(dǎo)致的轉(zhuǎn)換錯(cuò)誤。

圖 6. 使用 f:convertNumber 標(biāo)簽
使用 f:convertNumber 標(biāo)簽

自定義轉(zhuǎn)換
如果需要將字段數(shù)據(jù)轉(zhuǎn)換為特定于應(yīng)用程序的值對象,則需要自定義數(shù)據(jù)轉(zhuǎn)換,如下面例子所示:

  • String 轉(zhuǎn)換為 PhoneNumber 對象 (PhoneNumber.areaCode、PhoneNumber.prefix、 ...)。
  • String 轉(zhuǎn)換為 Name 對象 (Name.first、Name.last)。
  • String 轉(zhuǎn)換為 ProductCode 對象 (ProductCode.partNum、ProductCode.rev、 ...)。

要?jiǎng)?chuàng)建自定義轉(zhuǎn)換器,必須完成以步驟:

  1. 實(shí)現(xiàn) Converter 接口(也就是 javax.faxes.convert.Converter )。

  2. 實(shí)現(xiàn) getAsObject 方法,它將一個(gè)字段(字符串)轉(zhuǎn)換為一個(gè)對象(例如, PhoneNumber )。

  3. 實(shí)現(xiàn) getAsString 方法,它將一個(gè)對象(如 PhoneNumber )轉(zhuǎn)換為一個(gè)字符串。

  4. Faces 上下文中注冊自定義轉(zhuǎn)換器。

  5. <f:converter/> 標(biāo)簽在 JSP 中插入這個(gè)轉(zhuǎn)換器。

您可以自己看到如何在 JSF 應(yīng)用程序生命周期中加入這些步驟。在圖 7 中,JSF 在應(yīng)用請求值階段調(diào)用自定義轉(zhuǎn)換器的 getAsObject 方法。轉(zhuǎn)換器必須在這里將請求字符串轉(zhuǎn)換為所需的對象類型,然后返回這個(gè)對象,將它存儲(chǔ)在相應(yīng)的 JSF 組件中。如果該值被返回呈現(xiàn)在視圖中,那么 JSF 將在呈現(xiàn)響應(yīng)階段調(diào)用 getAsString 方法。這意味著轉(zhuǎn)換器還要負(fù)責(zé)將對象數(shù)據(jù)轉(zhuǎn)換回字符串表示形式。

圖 7. 自定義轉(zhuǎn)換器 getAsObject 和 getAsString 方法
getAsObject、getAsString 自定義轉(zhuǎn)換器方法

創(chuàng)建自定義轉(zhuǎn)換器
我們將使用一個(gè)案例分析來展示 Converter 接口、 getAsObject getAsString 方法的實(shí)現(xiàn),同時(shí)還將展示如何在 Faces 上下文中注冊這個(gè)轉(zhuǎn)換器。

這個(gè)案例分析的目的是將一個(gè)單字段字符串值轉(zhuǎn)換為一個(gè) PhoneNumber 對象。我們將一步一步地完成這個(gè)轉(zhuǎn)換過程。

第 1 步:實(shí)現(xiàn) Converter 接口
這一步實(shí)現(xiàn) Converter 接口。

            
              
import javax.faces.convert.Converter;
import org.apache.commons.lang.StringUtils;
...

public class PhoneConverter implements Converter {
	...

}

            
          

第 2 步:實(shí)現(xiàn) getAsObject 方法
這一步將一個(gè)字段值轉(zhuǎn)換為一個(gè) PhoneNumber 對象。

            
              
public class PhoneConverter implements Converter {

	...

	public Object getAsObject(FacesContext context, UIComponent component, String value) {
	    if (StringUtils.isEmpty(value)){ return null;}

	    PhoneNumber phone = new PhoneNumber();

	    String [] phoneComps = StringUtils.split(value," ,()-");

	    String countryCode = phoneComps[0];

	    phone.setCountryCode(countryCode);

	    if ("1".equals(countryCode)){
	        String areaCode = phoneComps[1];
	        String prefix = phoneComps[2];
	        String number = phoneComps[3];
	        phone.setAreaCode(areaCode);
	        phone.setPrefix(prefix);
	        phone.setNumber(number);
	    }else {
	        phone.setNumber(value);
	    }

	    return phone;
	}	
}

            
          

第 3 步:實(shí)現(xiàn) getAsString 方法
這一步將一個(gè) PhoneNumber 對象轉(zhuǎn)換為一個(gè)字符串。

            
              
public class PhoneConverter implements Converter {

	...

	public String getAsString(FacesContext context, 
							  UIComponent component, Object value) {
		return value.toString();
	}


}

public class PhoneNumber implements Serializable {

	...

	public String toString(){
		if (countryCode.equals("1")){
			return countryCode + " " + areaCode 
							   + " " + prefix + " " + number;
		}else{
			return number;
		}
	}


}

            
          

第 4 步:在 faces 上下文中注冊自定義轉(zhuǎn)換器
第 4 步可以以兩種方式執(zhí)行。第一種選擇使用(比如) arcmind.PhoneConverter 的 id 來注冊 PhoneConverter 類。JSP 頁中的 <f:converter/> 標(biāo)簽會(huì)使用這個(gè) id。下面是第 4 步的選項(xiàng) 1 的代碼:

            
              
<converter>

  <converter-id>arcmind.PhoneConverter</converter-id>

  <converter-class>com.arcmind.converters.PhoneConverter</converter-class>
      
</converter>

            
          

另一種方法是注冊 PhoneConverter 類來自動(dòng)處理所有 PhoneNumber 對象,如下所示。

            
              
<converter>
  
  <converter-for-class>com.arcmind.value.PhoneNumber</converter-for-class>

  <converter-class>com.arcmind.converters.PhoneConverter</converter-class>
      
</converter>

            
          

第 5 步:在 JSP 中使用轉(zhuǎn)換器標(biāo)簽?
自然,下一步的執(zhí)行取決于所選的注冊方法。如果選擇使用 arcmind.PhoneConverter 的 id 來注冊 PhoneConverter 類,那么就使用 <f:converter/> 標(biāo)簽,如下所示。

            
              
<h:inputText id="phone" value="#{UserRegistration.user.phone}">
        <f:converter  converterId="arcmind.PhoneConverter" />
</h:inputText>  

            
          

如果選擇注冊 PhoneConverter 類來 自動(dòng) 處理所有 PhoneNumber ,那么就不需要在 JSP 頁中使用 <f:converter/> 標(biāo)簽。下面是第 5 步的不帶轉(zhuǎn)換器標(biāo)簽的代碼。

            
              
<h:inputText id="phone" value="#{UserRegistration.user.phone}">
        [Look mom no converter!]
</h:inputText>  

            
          

這樣,我們已經(jīng)完成了這個(gè)示例應(yīng)用程序的轉(zhuǎn)換處理代碼!到目前為止完成的應(yīng)用程序如下所示。

圖 8. 帶有轉(zhuǎn)換處理的示例應(yīng)用程序
帶有轉(zhuǎn)換處理的示例應(yīng)用程序

JSF 驗(yàn)證
如前所述,JSF 驗(yàn)證可以確保應(yīng)用程序數(shù)據(jù)包含預(yù)期的內(nèi)容,例如:

  • java.util.Date 為 MM/yyyy 格式。
  • Float 在 1.0 和 100.0 之間。

在 JSF 中有 4 種驗(yàn)證:

  • 自帶驗(yàn)證組件。
  • 應(yīng)用程序級驗(yàn)證。
  • 自定義驗(yàn)證組件(它實(shí)現(xiàn)了 Validator 接口)。
  • 在 backing bean 中的驗(yàn)證方法(內(nèi)聯(lián))。

我們將在下面的討論中介紹并展示每一種形式。

JSF 驗(yàn)證生命周期和組件
圖 9 顯示了用戶注冊表單中名字字段的生命周期案例分析。代碼引用被有意解釋為偽代碼(pseudo-code)。

圖 9. JSF 生命周期中的驗(yàn)證
JSF 生命周期中的驗(yàn)證過程展示

下面是 JSF 提供的一組標(biāo)準(zhǔn)驗(yàn)證組件:

  • DoubleRangeValidator :組件的本地值必須為數(shù)字類型,必須在由最小和/或最大值所指定的范圍內(nèi)。

  • LongRangeValidator :組件的本地值必須為數(shù)字類型,并且可以轉(zhuǎn)換為長整型,必須在由最小和/或最大值所指定的范圍內(nèi)。

  • LengthValidator :類型必須為字符串,長度必須在由最小和/或最大值所指定的范圍內(nèi)。

標(biāo)準(zhǔn)驗(yàn)證
在我們的示例應(yīng)用程序中,用戶的年齡可以是任意有效的整數(shù)(byte、short、int)。因?yàn)閷⒛挲g設(shè)置為(比如說) -2 是無意義的,所以可能要對這個(gè)字段添加一些驗(yàn)證。下面是一些簡單的驗(yàn)證代碼,用以確保年齡字段中的數(shù)據(jù)模型完整性:

            
              
<h:inputText id="age" value="#{UserRegistration.user.age}">
          <f:validateLongRange maximum="150"
                                  minimum="0"/>
</h:inputText>

            
          

完成年齡字段后,可能希望指定對名字字段的長度加以限制。可以像這樣編寫這個(gè)驗(yàn)證:

            
              
<h:inputText id="firstName"
                value="#{UserRegistration.user.firstName}">
	<f:validateLength minimum="2" 
						 maximum="25" />
</h:inputText>

            
          

圖 10 顯示了由上面標(biāo)準(zhǔn)驗(yàn)證示例所生成的默認(rèn)詳細(xì)驗(yàn)證消息。

圖 10. 標(biāo)準(zhǔn)驗(yàn)證錯(cuò)誤消息
帶有標(biāo)準(zhǔn)驗(yàn)證錯(cuò)誤消息的示例應(yīng)用程序的屏幕快照

盡管 JSF 自帶的驗(yàn)證在許多情況下都可以滿足,但是它有一些局限性。在處理電子郵件驗(yàn)證、電話號碼、URL、日期等數(shù)據(jù)時(shí),有時(shí)編寫自己的驗(yàn)證器會(huì)更好一些,不過我們將在稍后對此進(jìn)行討論。

應(yīng)用程序級驗(yàn)證
在概念上,應(yīng)用程序級驗(yàn)證實(shí)際上是業(yè)務(wù)邏輯驗(yàn)證。JSF 將表單和/或字段級驗(yàn)證與業(yè)務(wù)邏輯驗(yàn)證分離開。應(yīng)用程序級驗(yàn)證主要需要在 backing bean 中添加代碼,用這個(gè)模型確定綁定到模型中的數(shù)據(jù)是否合格。對于購物車,表單級驗(yàn)證可以驗(yàn)證輸入的數(shù)量是否有效,但是需要使用業(yè)務(wù)邏輯驗(yàn)證檢查用戶是否超出了他或者她的信用額度。這是在 JSF 中分離關(guān)注點(diǎn)的另一個(gè)例子。

例如,假定用戶單擊了綁定到某個(gè)操作方法的按鈕,那么就會(huì)在調(diào)用應(yīng)用程序階段調(diào)用這個(gè)方法(有關(guān)的細(xì)節(jié),請參見上面的 圖 1 )。假定在更新模型階段進(jìn)行了更新,那么在對模型數(shù)據(jù)執(zhí)行任何操縱之前,可以添加一些驗(yàn)證代碼,根據(jù)應(yīng)用程序的業(yè)務(wù)規(guī)則檢查輸入的數(shù)據(jù)是否有效。

例如,在這個(gè)示例應(yīng)用程序中,用戶單擊了 Register 按鈕,這個(gè)按鈕被綁定到應(yīng)用程序控制器的 register() 方法。我們可以在 register() 方法中添加驗(yàn)證代碼,以確定名字字段是否為 null。如果該字段為 null,那么還可以在 FacesContext 中添加一條消息,指示相關(guān)組件返回到當(dāng)前頁。

其實(shí)它現(xiàn)在并不是業(yè)務(wù)規(guī)則邏輯的一個(gè)好例子。更好的例子是檢查用戶是否超出了她或者她的信用額度。在該例中,不是檢查字段是否為空,我們可以調(diào)用模型對象的方法來確保當(dāng)前用戶已經(jīng)不在系統(tǒng)中。

圖 11 描繪了這個(gè)過程。

圖 11. 應(yīng)用程序級驗(yàn)證
應(yīng)用程序級驗(yàn)證圖

注意在 register() 方法中,消息是如何以 ${formId}:${fieldId} 的形式添加到 FacesContext 中的。圖 12 顯示了消息與組件 id 之間的關(guān)系。

圖 12. 驗(yàn)證消息
驗(yàn)證消息

應(yīng)用程序級驗(yàn)證的優(yōu)缺點(diǎn)
應(yīng)用級驗(yàn)證非常直觀并且容易實(shí)現(xiàn)。不過,這種形式的驗(yàn)證是在其他形式的驗(yàn)證(標(biāo)準(zhǔn)、自定義、組件)之后發(fā)生的。

應(yīng)用程序級驗(yàn)證的優(yōu)點(diǎn)如下:

  • 容易實(shí)現(xiàn)。
  • 不需要單獨(dú)的類(自定義驗(yàn)證器)。
  • 不需要頁編寫者指定驗(yàn)證器。

應(yīng)用程序級驗(yàn)證的缺點(diǎn)如下:

  • 在其他形式的驗(yàn)證(標(biāo)準(zhǔn)、自定義)之后發(fā)生。
  • 驗(yàn)證邏輯局限于 backing bean 方法,使得重用性很有限。
  • 在大型應(yīng)用程序和/或團(tuán)隊(duì)環(huán)境中可能難于管理。

最終,應(yīng)用程序級驗(yàn)證只應(yīng)該用于那些需要業(yè)務(wù)邏輯驗(yàn)證的環(huán)境中。

自定義驗(yàn)證組件
對于標(biāo)準(zhǔn) JSF 驗(yàn)證器不支持的數(shù)據(jù)類型,則需要建立自己的自定義驗(yàn)證組件,其中包括電子郵件地址和郵政編碼。如果需要明確控制顯示給最終用戶的消息,那么還需要建立自己的驗(yàn)證器。在 JSF 中,可以創(chuàng)建可在整個(gè) Web 應(yīng)用程序中重復(fù)使用的可插入驗(yàn)證組件。

MyFaces 是一個(gè) JSF 的開放源代碼實(shí)現(xiàn),它提供了許多額外的驗(yàn)證器,其中包括一些 JSF 中所沒有的驗(yàn)證器。要了解 MyFaces 的內(nèi)容,請參閱 參考資料

創(chuàng)建自定義驗(yàn)證器的步驟如下,我們將一步步地分析:

  1. 創(chuàng)建一個(gè)實(shí)現(xiàn)了 Validator 接口的類 ( javax.faces.validator.Validator )。

  2. 實(shí)現(xiàn) validate 方法。

  3. 在 faces-confix.xml 文件中注冊自定義驗(yàn)證。

  4. 在 JSP 頁中使用 <f:validator/> 標(biāo)簽。

下面是創(chuàng)建自定義驗(yàn)證器的分步示例代碼。

第 1:實(shí)現(xiàn) Validator 接口
第一步是實(shí)現(xiàn) Validator 接口。

            
              
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
...
public class ZipCodeValidator implements Validator{
	private boolean plus4Required;
	private boolean plus4Optional;

	/** Accepts zip codes like 85710 */
	private static final String ZIP_REGEX = "[0-9]{5}";
	
	/** Accepts zip code plus 4 extensions like "-1119" or " 1119" */
	private static final String PLUS4_REQUIRED_REGEX = "[ |-]{1}[0-9]{4}";
	
	/** Optionally accepts a plus 4 */
	private static final String PLUS4_OPTIONAL_REGEX = "([ |-]{1}[0-9]{4})?";
	...
}

            
          

第 2 步:實(shí)現(xiàn)驗(yàn)證方法
接下來,需要實(shí)現(xiàn) validate 方法。

            
              
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
		 
      /* Create the correct mask */
      Pattern mask =  null; 
		 
      /* more on this method later */
      initProps(component);

      if (plus4Required){
            mask = Pattern.compile(ZIP_REGEX + PLUS4_REQUIRED_REGEX);
      } else if (plus4Optional){
            mask = Pattern.compile(ZIP_REGEX + PLUS4_OPTIONAL_REGEX);
      } else if (plus4Required && plus4Optional){
            throw new IllegalStateException("Plus 4 is either optional or required");
      }
      else {
            mask = Pattern.compile(ZIP_REGEX);
      }

            /* Get the string value of the current field */
      String zipField = (String)value; 
		 	
            /* Check to see if the value is a zip code */
    Matcher matcher = mask.matcher(zipField);
	     
    if (!matcher.matches()){
	     	
       FacesMessage message = new FacesMessage();
       message.setDetail("Zip code not valid");
       message.setSummary("Zip code not valid");
       message.setSeverity(FacesMessage.SEVERITY_ERROR);
       throw new ValidatorException(message);
    }
 }

            
          

第 3 步:在 FacesContext 中注冊自定義驗(yàn)證器
您現(xiàn)在應(yīng)該熟悉在 FacesContext 中注冊自定義驗(yàn)證器的代碼了。

            
              
  <validator>

    <validator-id>arcmind.zipCodeValidator</validator-id>

    <validator-class>com.arcmind.jsfquickstart.validation.ZipCodeValidator</validator-class>

  </validator>

            
          

第 4 步:在 JSP 中使用 <f:validator/> 標(biāo)簽
<f:validator/> 標(biāo)簽聲明使用 zipCodeValidator <f:attribute/> 標(biāo)簽將 plus4Optional 屬性設(shè)置為 true 。注意,它定義了 inputText 組件的屬性,而 不是 驗(yàn)證器的屬性!

            
              
  <h:inputText id="zipCode" value="#{UserRegistration.user.zipCode}">
      <f:validator validatorId="armind.zipCodeValidator"/>
      <f:attribute name="plus4Optional" value="true"/>
  </h:inputText>

            
          

為了讀取 zipCode inputText 組件的 plus4Optional 屬性,請完成以下步驟::

            
              
private void initProps(UIComponent component) {
  Boolean optional = Boolean.valueOf((String) component.getAttributes().
                                   get("plus4Optional"));
  Boolean required = Boolean.valueOf((String) component.getAttributes().
                                   get("plus4Required"));
  plus4Optional = optional==null ? plus4Optional : 
					     optional.booleanValue();
  plus4Required = required==null ? plus4Optional : 
					     required.booleanValue();
}

            
          

總體而言,創(chuàng)建自定義驗(yàn)證器是相當(dāng)直觀的,并且可以使該驗(yàn)證在許多應(yīng)用程序中重復(fù)使用。缺點(diǎn)是必須創(chuàng)建一個(gè)類,并在 faces 上下文中管理驗(yàn)證器注冊。不過,通過創(chuàng)建一個(gè)使用這個(gè)驗(yàn)證器的自定義標(biāo)簽,使其看上去像是一個(gè)自帶的驗(yàn)證,可以進(jìn)一步實(shí)現(xiàn)自定義驗(yàn)證器。對于常見的驗(yàn)證問題,如電子郵件驗(yàn)證,這種方法可以支持這樣一種設(shè)計(jì)理念,即代碼重用和一致的應(yīng)用程序行為是最重要的。

backing bean 中的驗(yàn)證方法
作為創(chuàng)建單獨(dú)的驗(yàn)證器類的替代方法,可以只在 backing bean 的方法中實(shí)現(xiàn)自定義驗(yàn)證,只要這個(gè)方法符合 Validator 接口的 validate 方法的參數(shù)簽名即可。例如,可以編寫以下方法:

            
              
[SomeBackingBean.java]

public void validateEmail(FacesContext context, 
                          UIComponent toValidate,
                          Object value) {
    String email = (String) value;

    if (email.indexOf('@') == -1) {
       ((UIInput)toValidate).setValid(false);

        FacesMessage message = new FacesMessage("Invalid Email");
        context.addMessage(toValidate.getClientId(context), message);
    }

}

            
          

之后,可通過如下所示的 validator 屬性在 JSF 中使用這個(gè)方法:

            
              
  <h:inputText id="email" 
               value="#{UserRegistration.user.email}"
               validator="#{UserRegistration.validateEmail}"   
               required="true">
  </h:inputText>

            
          

JSF 用 validateEmail 方法對綁定到 user.email 模型屬性的 inputText 組件值進(jìn)行自定義驗(yàn)證。如果電子郵件格式無效,那么就在相關(guān)組件的 faces 上下文中添加消息。考慮到這種驗(yàn)證方法實(shí)際上是 backing bean 的一部分,為什么通常必須用某個(gè)值與相關(guān)組件的關(guān)聯(lián)來評估該值,而不是直接檢查本地 bean 屬性呢?線索就在前面的生命周期圖中。如果現(xiàn)在不能馬上找到答案,也不要擔(dān)心,我們將在本文的最后對此加以說明。

默認(rèn)驗(yàn)證
注意上面 email 標(biāo)簽的 required 屬性。利用 required 屬性是一種 默認(rèn) 驗(yàn)證形式。如果這個(gè)屬性是 true ,那么相應(yīng)的組件必須有一個(gè)值。一個(gè)重要的說明:如果 required 屬性為 false ,那么就不用對這個(gè)標(biāo)簽/組件指派驗(yàn)證,這樣,JSF 將跳過對這個(gè)組件的驗(yàn)證,并讓值和組件的狀態(tài)保持不變。

圖 13 概述了我們討論過的驗(yàn)證形式。

圖 13. 驗(yàn)證視圖
JSF 中驗(yàn)證形式的一覽圖

自定義消息
您可能注意到了,JSF 提供的默認(rèn)轉(zhuǎn)換和驗(yàn)證消息非常長,這會(huì)讓那些總是輸入無效表單數(shù)據(jù)的最終用戶感到困惑和惱火。幸運(yùn)的是,您可以通過創(chuàng)建自己的消息資源綁定來改變 JSF 提供的默認(rèn)消息。jsf-impl.jar (或類似的文件中)中包含了一個(gè) message.properties 文件,該文件包含圖 14 所示的默認(rèn)消息。

圖 14. 默認(rèn) JSF 轉(zhuǎn)換和驗(yàn)證消息
JSF 的默認(rèn)轉(zhuǎn)換和驗(yàn)證消息

通過創(chuàng)建自己的 message.properties 文件并斷開指定場所的 faces 上下文中綁定的消息資源,您可以更改默認(rèn)消息,如圖 15 所示。

圖 15. 取消消息資源綁定
消息綁定

關(guān)于在 JSF 中創(chuàng)建自定義轉(zhuǎn)換和驗(yàn)證消息的更多內(nèi)容請參前閱 參考資料

處理 JSF 生命周期
我們在本文前面留下了一些問題讓您考慮,現(xiàn)在可以解決它們了!我們提到的一件事是對 UICommand 按鈕使用 immediate 屬性 ,比如 commandLink 或者 commandButtons 。現(xiàn)在請您考慮希望在什么樣的場景中跳過驗(yàn)證。

基本上只要用戶需要輸入數(shù)據(jù),就需要對這個(gè)數(shù)據(jù)進(jìn)行驗(yàn)證。不過,如果整個(gè)數(shù)據(jù)項(xiàng)是可選的,那么就不需要進(jìn)行驗(yàn)證。一種避免 JSF 生命周期的驗(yàn)證階段的方法是利用 UICommand 組件的 immediate 屬性,該屬性可以在處理驗(yàn)證階段 之前 的應(yīng)用請求值階段期間(而不是在處理驗(yàn)證階段 之后 的調(diào)用應(yīng)用程序階段)強(qiáng)制調(diào)用這個(gè)操作。

immediate 屬性允許您通過標(biāo)準(zhǔn)瀏覽規(guī)則控制頁流程,并繞過驗(yàn)證。可以針對特定的場景實(shí)現(xiàn)這項(xiàng)技術(shù),比如帶有可選步驟和/或表單的在線向?qū)Вㄈ绠?dāng)用戶單擊 Skip 按鈕以進(jìn)入下一視圖),或者在用戶因?yàn)槟撤N原因而取消某個(gè)表單的情況下。

我們在本文中留下的第二個(gè)問題是:既然驗(yàn)證方法實(shí)際上是 backing bean 的一部分,那么為什么通常必須利用組件關(guān)聯(lián)來判斷它的值。請參閱前面的 JSF 應(yīng)用程序生命周期 ,看看您能否找到答案。

這里的密訣是:盡管 validateEmail 嵌入驗(yàn)證方法是實(shí)際的 backing bean 的一部分,但是該方法必須通過組件關(guān)聯(lián)來引用這,而不是直接訪問本地屬性來引用值。由于驗(yàn)證發(fā)生在組件值綁定到模型 之前 (在更新模型值階段),所以模型處于未知狀態(tài)。因此,必須編寫嵌入自定義驗(yàn)證邏輯,就像使用一個(gè)自定義 Validator 對象處理驗(yàn)證一樣。這也解釋了維護(hù)相同方法簽名的需求。

這些尚待解決的枝節(jié)問題有什么意義呢,當(dāng)然,它們最終將我們帶回 JSF 應(yīng)用程序生命周期。將這些問題匯總在一起,就能體現(xiàn)充分理解生命周期的重要性 —— 向后、向前或由內(nèi)向外,這樣您就可以在需要的時(shí)候操縱它。

結(jié)束語
在本文中我們討論了相當(dāng)多的 JSF 轉(zhuǎn)換和驗(yàn)證的基本內(nèi)容。事實(shí)上,我們討論了在自己的應(yīng)用程序中使用這些過程需要知道的大部分內(nèi)容(至少對這個(gè)版本的 JSF 而言)!

當(dāng)然,我們不可能討論到 所有內(nèi)容 。例如,您可能想要了解 MyFaces (請參閱 參考資料 )中 JSF 沒有提供、或者這里沒有討論到的驗(yàn)證器組件。此外,雖然我們討論了大多數(shù)常用的轉(zhuǎn)換和驗(yàn)證技術(shù),但還有一些沒有包含在內(nèi)。例如,在編寫自定義組件時(shí),可以在組件的解碼/編碼過程中直接處理轉(zhuǎn)換和/或驗(yàn)證(取決于組件的類型及其功能),但是我們只能將對自定義組件開發(fā)的更深入討論留到以后進(jìn)行了。

其他要牢記的是轉(zhuǎn)換和驗(yàn)證不一定會(huì)很好地協(xié)同工作。轉(zhuǎn)換將字符串轉(zhuǎn)換為對象,而大多數(shù)標(biāo)準(zhǔn)驗(yàn)證是對字符串進(jìn)行的。因此,在同時(shí)使用自定義轉(zhuǎn)換和驗(yàn)證必須格外小心。例如, PhoneNumber 對象不能與長度驗(yàn)證器一起使用。在這種情況下,要么編寫自定義驗(yàn)證器,要么在自定義轉(zhuǎn)換器中添加一個(gè)特別的驗(yàn)證邏輯。我們偏向后一種方法,因?yàn)樗屛覀兛梢詫⒆远x轉(zhuǎn)換器(自帶驗(yàn)證邏輯)與特定的對象類型相關(guān)聯(lián),并讓 JSF 處理這種對象類型。JSF 自動(dòng)為我們做這項(xiàng)工作,不需要在 JSP 中包含任何特定的轉(zhuǎn)換器 id。(當(dāng)然,有人會(huì)稱它為懶惰編程,它也不是對所有用例都適用的最佳解決方案。)

我們認(rèn)為本月文章中的討論再次聲明了以下這點(diǎn),即 JSF 提供了一種靈活的、強(qiáng)大的可插入式 Web 應(yīng)用程序開發(fā)框架。除了標(biāo)準(zhǔn)轉(zhuǎn)換器和驗(yàn)證器之外,JSF 還可以促進(jìn)同時(shí)滿足應(yīng)用程序和框架開發(fā)人員的要求的自定義實(shí)現(xiàn)。最終,要由您來確定選擇何種轉(zhuǎn)換和驗(yàn)證策略。JSF 使您能夠在原型制造階段很快、很容易地上手(標(biāo)準(zhǔn)轉(zhuǎn)換器、驗(yàn)證器、內(nèi)部驗(yàn)證等),并在以后的開發(fā)階段移植到更復(fù)雜的生產(chǎn)解決方案中(自定義對象、自定義消息等)。JSF 生命周期在所有階段都提供了可靠的基礎(chǔ)設(shè)施,始終如一地保證數(shù)據(jù)模型的完整性。

JSF 轉(zhuǎn)換與驗(yàn)證


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 花垣县| 竹溪县| 昌乐县| 积石山| 德兴市| 襄城县| 孟州市| 普兰店市| 西华县| 乌拉特后旗| 上思县| 赤水市| 鸡东县| 宜君县| 池州市| 行唐县| 石城县| 东乡族自治县| 永仁县| 抚顺市| 新化县| 牟定县| 尖扎县| 平乡县| 通化县| 南昌县| 九江市| 涡阳县| 金乡县| 邹平县| 宜丰县| 郓城县| 乌兰察布市| 西畴县| 荥阳市| 威信县| 兰州市| 吐鲁番市| 凤凰县| 偏关县| 仲巴县|