第203期 / September 5, 2014

分享到臉書!分享到維特!分享到噗浪!分享到Google+!分享到微博!轉寄友人友善列印

JSF生命週期與Immediate參數的設定

作者/黃瀚明

[發表日期:2014/9/5]

表單的資料驗證範例

驗證使用者輸入的資料是否合法幾乎是每個工程師必會面臨的課題,例如撰寫網頁時常需處理表單的輸入,此時資料驗證便成為不可或缺的一環,然而這種繁複且瑣碎的工作不僅加重工程師的負擔,也讓工程師們無法專注於商業邏輯的開發,因此許多人會直接使用框架內建的資料驗證機制,減少重工的機會,也加快開發流程。

無庸置疑的, JSF也有內建的資料驗證機制,我們甚至只要在前端的Facelets頁面設定好驗證規則,就可完成表單的資料驗證。當然,想要設計更複雜的資料驗證機制,JSF也支援讓我們使用數種不同方式來完成資料驗證。我們首先看一個非常小的資料驗證範例:


《圖一》


在這個範例中,我們讓使用者在首頁(index.xhtml)點擊”設定使用者名稱”後,移至(Forward) setUserName.xhtml頁面,輸入必填欄位”使用者名稱”,按下確定後在後端印出使用者輸入的字串,並回到index.xhtml,若未填入使用者名稱,則秀出提示字串提醒使用者輸入名稱。程式碼如下所示。


《圖二》


在前端,工程師只需在inputText元件中指定required=”true”以及requiredMessage,便可讓inputText元件成為必填欄位,當使用者未輸入資料便送出表單時,會出現錯誤提示,如下圖。


《圖三》


而由於使用者未輸入資料,導致資料驗證失敗,後端的Console視窗當然也沒有印出任何東西,這意味著在後端,MyBean的方法doSubmit並沒有被執行:


《圖四》


截至目前為止,我們所建構的小型表單都如預期中的運作,但若您實際建構此範例表單網頁,並在未輸入使用者名稱的情況下點擊取消鈕,會發現JSF同樣也針對了取消按鈕做了資料驗證:


《圖五》


當然,這並不是我們所期盼的。讓我們回頭來看看原始碼:


《圖六》


我們在Facelets撰寫Button元件時,並沒有告知JSF這兩個Button實際上有何不同,就原始碼來看,此二者的差別只不過是在指定後端要執行的方法時,指定了不同的方法而已。所以JSF不會知道Cancel按鈕代表的是取消送出表單,當然也就執行了內建的資料驗證方法,進而秀出錯誤訊息要求使用者輸入名稱。要讓Cancel按鈕如期運作,必須在該元件加入一個參數:immediate=”true”。


《圖七》


如此一來,即便使用者在未輸入使用者名稱的情況下點擊取消鈕,也不會跳出錯誤訊息,並且能夠正常的返回index.xhtml了:


《圖八》


深入探討Immediate=”true”

或許您在之前就已經知道要讓Cancel按鈕正常執行,必須在該元件的參數加上immediate=”true”;然而將Immediate這個參數設定為true後,JSF究竟做了哪些事,可能您還沒有時間細究。實際上immediate參數的設定與JSF生命週期有極大的關係,本文接下來會討論immediate=”true”與JSF生命週期的關係,讓您在使用immediate=”true”這個參數時更加得心應手,同時也對JSF生命週期有初步的認識。

JSF的生命週期

在了解Immediate與JSF生命週期的關係前,我們首先說明JSF的生命週期。如下圖所示,JSF的生命週期扣除Event之後,共有六大生命週期,分別為:Restore View、Apply Requests、Process Validations、Update Model Values、Invoke Application、Render Response。


《圖九》


接下來將針對此六大生命週期簡單描述在各階段JSF所執行的動作。

一、Restore View Phase

在此階段,JSF將載入頁面視圖,並把Event Handlers及Validators與頁面上的元件連結,同時將視圖儲存在FacesContext裡。如果是第一次載入頁面,由於沒有既有的視圖可供載入,JSF將創建一個新的視圖,且由於沒有提交表單,生命週期會直接跳到最後一個階段,也就是Render Response階段(參圖中方塊A)。如果不是第一次載入頁面,因為先前已經產生視圖,JSF會從Client端或Server端載入並回復視圖。

二、Apply Request Values Phase

JSF回復視圖之後,會從Request Parameter裡取得使用者在表單內輸入的值,並且將這些值儲存在對應的元件中。如果使用者輸入的數值轉型失敗,則會將這個元件的Error Message儲存進FacesContext的佇列中,並在Render Response Phase與其他驗證錯誤訊息一起show出來。

請注意上述提及的”轉型”,是依據後端綁定的物件屬性的型態來轉型,例如若Bean裡面的屬性是int,轉型時就會把使用者輸入的值由String轉成int,因此若使用者輸入了無法轉型成int的字串(例如:syscom),就會發生轉型失敗。

三、Process Validations Phase

在這個階段,JSF會處理元件上的所有Validator。JSF會檢查儲存在元件內的數值是否符合設定的驗證規則,如果不合規則,JSF會添加一個錯誤訊息到FacesContext裡,同時生命週期會直接跳到最後一個階段(Render Response Phase),如此一來,頁面會被重新繪製,而錯誤訊息也會被秀出來(參圖中方塊C)。如果在前一階段(Apply Request Values Phase)時轉型失敗,在此時轉型失敗的錯誤訊息也會一併出現。若數值通過驗證,但該數值與舊有的數值不同時,JSF將會執行我們在valueChangeListener參數中設定的方法。

四、Update Model Values Phase

資料驗證成功之後,JSF會試圖把元件裡儲存的數值經由Backing Bean的Setter方法設定到後端的Backing Bean裡面,如果元件中儲存的數值沒有辦法成功轉型成Backing Bean裡屬性的型態,則生命週期會直接跳到Render Response Phase讓頁面重新載入以印出錯誤訊息(參圖中方塊D)。

五、Invoke Application Phase

此階段是商業邏輯真正執行的階段,JSF會執行我們指定的商業邏輯,也就是我們在元件中action參數所指定的方法。

六、Render Response Phase

在此階段JSF會建立視圖,並委由適當的資源來繪製頁面,例如若前端是使用JSP頁面,JSF會委由JSP container來載入畫面。如果這次請求是初始請求,頁面上的元件會被加到元件樹上。若不是初始請求,就不必再加一次,因為這些元件在之前就已被加到元件樹中了。而這些元件的值會經由Backing Bean所設定的Getter方法所取得。

若在前述階段有發生錯誤的話,JSF會載入原始的頁面,但若頁面中包含message或messages標籤的話,錯誤訊息就會藉由這些標籤秀出來。
當頁面載入完成之後,Response的狀態就會被儲存,以利後續的Request存取,另外Restore View Phase也可以存取這個狀態。

Immediate=true與JSF生命週期

為了能夠清楚的了解JSF生命週期的實際執行過程,我們撰寫一個生命週期的監聽器,在每個生命周期的開始與結束時印出訊息,以便觀察JSF生命週期的執行狀態。以下是監聽器的程式碼:


《圖十》


另外,我們需要在faces-config.xml裡面指定這個監聽器,讓JSF能夠呼叫這個監聽器。請在faces-config.xml輸入下列設定(*faces-config.xml的建立方法請參註1):


《圖十一》


我們沿用本文第一段的表單資料驗證程式來觀察JSF生命週期。設定完成之後,該程式在正常流程下會印出下列資訊:


《圖十二》


JSF依序印出了每個生命周期階段的名稱,在Invoke_application這個階段則執行了我們寫在doSubmit方法裡的敘述句。

在未輸入使用者名稱就按下確認按鈕時,Console視窗則印出了下列資訊:


《圖十三》


可以看到JSF在Process_validations這個階段,由於驗證資料時發現使用者名稱沒有任何資料,所以驗證失敗,直接跳到了Render_response階段。

上面提及了Immediate=”true”實際上與JSF的生命週期有極大的關係,讓我們來看看按下了設定Immediate=”true”的取消按鈕時會發生什麼事。


《圖十四》


JSF在經歷了Restore_view、Apply_request_values階段之後,馬上跳到了Render_response階段。這是因為當使用者送出請求時,JSF會在Apply_Request_Values這個階段針對這些有設定Immediate的元件處理他們的驗證、轉型以及事件,所以我們會在Apply_Request_Values這邊看到JSF執行了我們在doCancel方法裡撰寫的敘述句(參流程圖方塊B)。

您可能會覺得有些疑惑,既然JSF還是會處理驗證及轉型,那麼按下取消時,應該還是會跳出錯誤,提醒使用者輸入使用者名稱才對吧?這邊要注意的是JSF是”針對有設定Immediate的元件處理驗證、轉型以及事件”,所以如果您想要讓JSF在我們送出表單時,針對輸入方塊處理驗證及轉型,就要在該輸入方塊設定Immediate=”true”,如此一來JSF就會在Apply_Request_Values這個階段執行該輸入方塊的驗證及轉型了。

註1:在NetBeans建立faces-config.xml檔

如果您使用的是NetBeans,在創建JSF2.0的應用程式時,應該不會出現faces-config.xml檔案,這是由於JSF2.0支援了JAVA的Annotation,以往需要在faces-config.xml裡面設定的諸多參數,現在都可以使用Annotation來指定,對程式設計師而言是個非常方便的功能。

在NetBeans,要建立faces-config.xml非常簡單,只要在專案上按下右鍵,並選擇New File(新增檔案),在篩選器中輸入Faces Config就能立即產生一個空的faces-config.xml檔案了:


《圖十五》


《圖十六》


參考連結

http://balusc.blogspot.tw/2006/09/debug-jsf-lifecycle.html
http://docs.oracle.com/javaee/5/tutorial/doc/bnaqq.html
http://docs.oracle.com/javaee/6/tutorial/doc/bnaqq.html
http://blog.jerryorr.com/2012/01/jsf-and-immediate-attribute-command.html