Tapestry 5輸入驗證的實現(xiàn)方法
表單輸入與驗證
表單輸入是任何應用的生命之血;這是一種從用戶收集有用信息的最有效的方式。不管它是一個搜索表單、一個登錄還是一個多頁的注冊向導,用戶借助表單在應用中真正地表達他們。
Tapestry 5輸入驗證在創(chuàng)建和驗證輸入有很好的表現(xiàn)。輸入驗證是聲明式的,意味首我們簡單地告訴Tapestry一個表單域應用哪種驗證,然后Tapestry會在服務器端(已實現(xiàn))與客戶端維護這種驗證。
最后,Tapestry不僅能夠將錯誤的信息表現(xiàn)給用戶,而且還能對表單域及其標注(labels)進行裝飾,標記它們包含錯誤(主要利用CSS效果)。
表單組件
Tapestry的表單支持的核心即是表單組件,表單組件封裝著其他所有表單域組件,如TextField、TextArea、Checkbox等等。
表單組件產(chǎn)生許多組件事件,我們可以給其提供事件處理方法。
呈現(xiàn)時,表單組件發(fā)出一個“prepare”通知,以使表單容器創(chuàng)建將要在表單中引用的表單域或屬性。如,這是一個創(chuàng)建被呈現(xiàn)的臨時實體對像或者載入一個源自數(shù)據(jù)庫的可被編輯的實體的好機會。
在Tapestry 5輸入驗證中,當用戶在客戶端提交表單時,服務器端會執(zhí)行一系列的步驟。
首先,表單呈現(xiàn)時會發(fā)出一個“prepare”通知。
其次,所有的表單域被激活并將從請求中得到相應的值,驗證它們及(如果有效)保存現(xiàn)有的變化。
對Tapestry 4的用戶:Tapestry 5不使用Tapestry 4中脆弱的表單刷新(form rewind)方法,而是在表單呈現(xiàn)時產(chǎn)生一個存放是否需要處理表單提交信息的隱藏域。
表單域流程處理完后,表單發(fā)出一個“validate”事件,這是一個執(zhí)行跨表單驗證(還不能公布其詳情)的機會。
然后,表單確定是否存在任何Tapestry 5輸入驗證錯誤。如果存在,表單提交失敗并發(fā)出一個"failure"事件。如果沒有驗證錯誤,些時將發(fā)出一個"success"事件。
最后,表單發(fā)出一個"submit"事件(邏輯上,它不考慮成功與否)。
跟蹤驗證錯誤
一個與表單關聯(lián)的就是驗證跟蹤器(ValidationTracker),它跟蹤著表單域對應的所有的用戶輸入與用戶驗證錯誤。跟蹤器可以通過跟蹤器參數(shù)提供給表單,不過很少用到。
表單(Form)包括兩個isValid() 和 getHasErrors()方法,用來查看表單驗證跟蹤器是否存在任何錯誤。
在我們的邏輯中,我們可以記錄驗證錯誤。表單(Form)包括兩個不同版本的recordError()方法,一個是指定一個表單域(Field,一個被所有表單元素組件實現(xiàn)的接口),另外一個是全局驗證錯誤("global" errors),與具體的表單域無關。
在請求間保存數(shù)據(jù)
因為其他的動作請求,表單提交的結果會向客戶端發(fā)出一個重定向來重新呈現(xiàn)頁面。驗證跟蹤器必須在請求間被持久化地(persistently)保存下來,否則所有的Tapestry 5輸入驗證信息會丟失(表單提供一個persisten形式的默認驗證跟蹤器)。
同樣地,組件更新單獨的表單域也應該被持久化。
比如,一個用來收集用戶名與密碼的登錄頁面,應該如下:
- public class Login
- {
- @Persist
- private String _userName;
- private String _password;
- @Inject
- private UserAuthenticator _authenticator;
- @Component(id = "password")
- private PasswordField _passwordField;
- @Component
- private Form _form;
- String onSuccess()
- {
- if (!_authenticator.isValid(_userName, _password))
- {
- _form.recordError(_passwordField, "Invalid user name or password.");
- return null;
- }
- return "PostLogin";
- }
- public String getPassword()
- {
- return _password;
- }
- public void setPassword(String password)
- {
- _password = password;
- }
- public String getUserName()
- {
- return _userName;
- }
- public void setUserName(String userName)
- {
- _userName = userName;
- }
- }
因為Form表單提交實際上是兩個請求(提交自己,然后重新呈現(xiàn)頁面),所以需要在兩個請求間持久化保存在_userName屬性里的值。屬性_password同樣需要,除非PasswordField組件從不呈現(xiàn)值。
注意onSuccess()方法不是公共的(public);事件處理方法可以具有任何的可見性,甚至私有的。包可見性(即無可見性修飾)比較常用,這時它允許組件可被相同包下的測試用例類測試。
假如Form先前沒有存在驗證錯誤,它僅產(chǎn)生一個"success"事件,這意味著沒有必要在方法的第一行寫上if (_form.getHasErrors()) return;這樣的語句。
最后,注意業(yè)務邏輯如何與表單驗證相關聯(lián)。UserAuthenticator服務用來保證userName 和 (文本的) password的有效性。當它返回false時,我們用Form組件來記錄一個錯誤。我們提供一個PasswordField實例作為它的第一個參數(shù),這保證了密碼表單域和它的標注(label)會在Form表單重新呈現(xiàn)時被修飾以表現(xiàn)錯誤給用戶看。
Tapestry 5輸入驗證的配置表單域及標注
頁面模板包含少量的Tapestry 標識(instrumentation):
- ﹤html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"﹥
- ﹤head﹥
- ﹤title﹥Login﹤/title﹥
- ﹤/head﹥
- ﹤body﹥
- ﹤h1﹥Please Login﹤/h1﹥
- ﹤t:form﹥
- ﹤t:errors/﹥
- ﹤t:label for="userName"/﹥:
- ﹤input t:type="TextField" t:id="userName" t:validate="required,minlength=3" size="30"/﹥
- ﹤br/﹥
- ﹤t:label for="password"/﹥:
- ﹤input t:type="PasswordField" t:id="password" t:validate="required,minlength=3" size="30"/﹥
- ﹤br/﹥
- ﹤input type="submit" value="Login"/﹥
- ﹤/t:form﹥
- ﹤/body﹥
- ﹤/html﹥
Tapestry的Form組件負責創(chuàng)建form表單提交所需的URL(這個Tapestry的責任,不是你的)。
Errors組件必須放在Form里,它將Form組件里表單域的所有錯誤信息作為一個列表輸出,并應用一此簡單的樣式使得結果更顯眼。
每一個表單域組件,比如TextField,與Label組件成對。Label將呈現(xiàn)一個與表單域相連的<label>元素。這個組件對方便使用非常重要,特別對那些有視覺障礙(殘疾)的用戶。它意味著你可以能過點擊標注(label)文本將光標移動到相應的表單域中。Label組件的for參數(shù)即是一組件的id。
對于TextField,我們提供了一個組件id為userName。我們可以指定一個value參數(shù),默認情況下這個value參數(shù)是匹配TextField的id,TextField的id又對應于組件容器(Login頁)的一個屬性(假如這個屬性存在)。
根據(jù)經(jīng)驗,你通常應該為表單域指定一個特定的id(這個id將會被用來呈現(xiàn)標簽的name與id屬性)。允許省略value參數(shù)有利于防止模板變得更加混亂。
用來驗證表單域的validate參數(shù)標識,是一個驗證器的名字列表。驗證器在Tapestry中被配置,可用的驗證器都是可擴展的。"required"是一個內置驗證器的名字,用以保證提交的值不為空串,此外,"minlen"用來保證值具有最小的指定長度。
Validate參數(shù)用t:前綴被放置在Tapestry的命名空間里。這不是嚴格需要的,只是讓模板有個良好的格式。然而,在Tapestry命名空間放置Tapestry特定的值保證了模板的有效性。
錯誤及修飾Errors and Decorations
注意:這部分并沒有更新到涉及客戶端驗證的介紹。
在Tapestry 5輸入驗證中,當你第一次激活Login頁里,表單域及表單會被正常呈現(xiàn)等待輸入:
注意這里的Label組件怎樣顯示表單域的文本名字。我們未曾做過任何顯式地配置,組件的id ("User Name" 和 "Password")已經(jīng)轉換成"User Name" 和 "Password",倒底發(fā)生了什么?。
如果你就這樣提交了表單,表單域就會違反了"required"約束,頁面將會重新顯示以呈現(xiàn)給用戶錯誤信息。
這里有兩個微妙事情,首先,Tapestry跟蹤了所有表單域的所有錯誤信息,Errors組件顯示在表單的最頂端;其次,默認的驗證修飾給標注(labels)和表單域的class屬性加上了t-error CSS樣式。Tapestry提供了默認的t-error CSS樣式使得它們變紅。
接下來,我們將在user name欄上填值,但不給password欄提供足夠的字符:User name欄正常,僅password欄有一個錯誤存在。PasswordField組件默認下通常顯示一空值,另外我們會看到僅password顯示在…
如果你輸入了足夠的字符后提交,我們會看到Login頁面是如何將邏輯錯誤加入到表單域的錯誤里:
這真是天衣無縫,基于應用的邏輯錯誤看起來感覺就像內置驗證器的行為。
【編輯推薦】