js
在任何一個基於Web的應用中,程式邏輯要求使用者提交需要校正的資訊,而應用的建立者則可以用兩種方式來檢測資料。第一種方法就是在用戶端校正,甚至在資訊提交到伺服器上之前也可以進行。通常,這種校正使用運行在用戶端網際網路瀏覽器內的JavaScript就可以完成。儘管表格將要提交,但是指令碼還是會檢查所有請求的域,如果不符合就彈出錯誤資訊。第二種方法就是在伺服器端校正。在執行對資料的任何操作之前,使用應用伺服器支援的技術來完成校正。
伺服器端的校正使伺服器更緊張,卻給予了程式員更多的控制,並且保證了資料一定會檢測。用戶端的校正很容易被使用者繞過(通過使JavaScript不能用),因此允許提交未檢測的資訊可以使速度更快,因為使用者不必傳送頁到伺服器,也不必從伺服器上擷取頁。
目前,越來越多的線上應用依賴於伺服器端的校正,主要原因是它保證了檢測並且提高了應用的安全性,另一個原因是伺服器硬體和軟體效能在近幾年內有較大的提高,還有一個原因是它慮及程式更大的靈活性並且往往易於實現。
目錄
校正進程
JSP標記
具有校正功能的JSP視圖
結論
代碼清單
在本文中,我將討論使用Java JSP標記庫進行伺服器端校正的唯一方法,並且簡短的描述了JSP標記庫的建立。我將在我以前文章"Applying MVC to web-based applications with JSP views"描述的Web應用上建立一個例子——一個在使用者提交ZIP代碼或者城市名之後顯示天氣資訊的簡單項目。該Web應用遵守Model-View-Controller (MVC) 結構並且使用Tomcat 應用伺服器。
校正進程
在企業級應用中,資料校正是安全進程的必需部分。應用不但必須捕捉空域或者局部域,他還必須預防錯誤項。如果資料提交到RDBMS 儲存空間或者任何其他持久性儲存空間內,並且已用在SQL語句中,使用者就能夠附帶(或者特意)提交導致SQL語句無效的壞資料。惡意使用者甚至能夠傳遞逸出字元串或者將特殊資料輸入到提交表格內,使他們執行Web應用開發人員原本沒有設計的任何操作。例如他們可以刪除來自資料庫的行或者表格,或者擷取他們想要的資訊。
我已經完成校正的JSP頁在MVC設計中屬於"Views" ,而MVC設計是線上天氣程式的一部分。 每個JSP視圖要麼提交資料要麼顯示資料。當提交帶有ZIP代碼或者城市名的表格時,資訊就被傳遞到伺服器。在伺服器上,Controller Servlet對象在特殊操作——來自於JSP的key 參數——的基礎之上執行一個操作,然後重新導向到下一個視圖。更多細節我將在稍後討論;現在我們先看看JSP標記的工作方式。
JSP標記
你可以將JSP標記看作是一個可由運行在伺服器上的Java代碼執行的自訂動作。在JSP中, 標記看起來就像一個標準的HTML標記,但是它的邏輯不在用戶端上執行,而是作為JSP轉換而成的Servlet 的一部分在伺服器端執行。每個標記封裝在一個單獨的類中,並且他的名字和參數屬性顯示在帶有tld 副檔名的特殊配置描述符檔案內。該檔案應該放在你的應用伺服器內的web應用目錄WEB-INF 下,並且在JSP中使用%@taglib ... %指令顯示該檔案,在web.xml 檔案中顯示該檔案。當JSP引擎遇到自訂標籤時,它檢測它自己是否知道標記類在哪裡,如果知道的話,就執行相應的代碼。標記類通常放在jar 檔案內並且放在WEB-INF 下的lib 目錄下。
Ex. ..\WEB-INF\mytags.tld - deployment descriptor file
Ex. ..\WEB-INF\lib\mytags.jar tag library (or a full directory structure)
為了展示如何使用標記進行校正,我建立了一個自訂JSP標記來校正ZIP代碼域,名叫notValidZip。標記可以執行任何動作,如HTML格式化、網域作業、或者甚至是資料庫查詢。為了其他的檢測,我使用了來自Coldbeans Software (http://www.servletsuite.com)的標記庫,也擁有了校正HTML表格域的非常真實的標記。Coldbeans Software可免費用於非商業用途。如果你需要在商業網站使用標記,你可以自己編寫或者購買。
下面是一個配置描述符檔案 validtag.tld ,它能夠處理我的notValidZip標記:
<tag>
<name>notValidZip</name>
<tagclass>com.cj.valid.notValidZip</tagclass>
<bodycontent>JSP</bodycontent>
<info>tests zip code</info>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>length</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
顯示結果為:它的名字叫 notValidZip,它的代碼在com.cj.valid.notValidZip 類中,他有兩個屬性,一個屬性必需,一個屬性備選。
為了讓Web應用知道我包括了一個自訂標籤庫,我將這個標記庫的資訊添加到web.xml 配置描述符檔案內。下面是代碼:
<taglib>
<taglib-uri>/WEB-INF/validtag.tld</taglib-uri>
<taglib-location>/WEB-INF/validtag.tld</taglib-location>
</taglib>
最後,為了把這個標記庫包括在JSP之內,我在我的JSP頁首添加了這行代碼:
<%@ taglib uri="/WEB-INF/validtag.tld" prefix="valTag" %>
現在,通過編寫標記首碼就可以使用來自該軟體包中的任何標記;例如:
<valTag:notValidZip value="<%=request.getParameter(\"ZIP\")%>">
<!-- body -->
</valTag:notValidZip>
標記通常有一個體和備選屬性。
<valTag:tagname attribute1="value1" attribute2="value2">
<!-- body -->
</valTag:tagname>
但是有些標記可能沒有體;例如
<valTag:tagname />.
在定義標記庫時你可以編寫你想要的任何首碼,但是標記名必須匹配tld 檔案中的名。
正如我所說,真實的校正邏輯是在Java類中進行的,這個Java類也被稱為"notValidZip",放在標記庫jar 檔案內。該標記類擴充了抽象TagSupport 類,並且引入了javax.servlet.jsp.JspException, javax.servlet.jsp.tagext. 標記和javax.servlet.jsp.tagext.TagSupport 類。主要的動作邏輯在doStartTag()方法內。 這個方法在JSP引擎遇到我的新標記時調用。
doStartTag()方法返回的整數代碼告訴JSP引擎它是否應該操作標記的體。見本文末尾的Listing 1 。
現在我們總結一下。首先,你編寫標記類,將它放在web應用'lib' 目錄下的jar 檔案中,為標記建立一個配置描述符檔案,使用它的位置資訊更新web.xml 檔案,最後使用taglib 指令將它包括進來,你就可以在你的JSP中使用它了。如果我需要在多個地方進行ZIP代碼校正,我只要將我的標記包括在多個頁中。見Listing 2.
具有校正功能的JSP視圖
通常要添加校正功能,程式員需要一個單獨的JSP頁,這個頁看起來像原版表格頁,如果表格域有問題,就顯示錯誤資訊,並且由伺服器顯示(重新導向到)該頁。我只在原版JSP頁中添加了錯誤邏輯。表格第一次顯示時不進行錯誤偵測。在提交動作中,表格將提交到他自身上,並且使用JSP標記校正域(在伺服器端, JSPs編譯成Servlets),並且如果一切正確,資料傳遞到主要的Controller Servlet.。否則,使用者在同一個JSP中可以看到錯誤資訊。
在JSP頁中,我有一個Java scriplet ,它可以建立邏輯標記變數"validate",並且如果有一個"validate"參數提交給該JSP,它的值為true。
<% boolean validate = ("true".equals((String)request.getParameter("validate"))); %>
基於此邏輯變數值,JSP使用我的標記進行校正。在頁面第一次裝載時,這個變數為false ,並且不需要校正。
為了提交頁面給它本身,然後將它重新導向到主要的Controller Servlet,我修改表格動作指向
<%=request.getRequestURI()%>
,並且預設JSP前進到標記
<jsp:forward page="../MainServlet" />。
在使用者提交表格時,他將所有的值都傳遞給同一個JSP,設定變數validate 的值為true,檢測使用的標記,並且如果資料得到認可,JSP將所有的值傳遞給Controller Servlet。
如果出現問題,標記體將執行,並且告訴JSP重新提交值到它本身上,然後根據錯誤的不同顯示新的(或者不同的)錯誤資訊。請注意:標記體內另外一個變數"success" 也要設定為false 。這個變數一開始設定為true ,並且只檢查是否已經執行過任何標記體。這就保證了只有當"validate" 和 "success"變數都為真時才傳遞表格。見Listing 2.
如果你有多個域需要校正,來自標記體的錯誤資訊將顯示不正確的域,這就減少了使用者糾正和重新提交的次數。
錯誤的ZIP代碼產生如下頁面:
正確的ZIP代碼產生以下頁面:
結論
我輸入到JSP中的附加邏輯可以在此JSP頁面中執行校正操作,將該邏輯與標記結合使用,就可以為伺服器端的資料校正建立一個簡單而又非常可再用的方案,而且不用使用多個JSP或者Servlets。校正任何一種類型的域只要一個標記,你可以擁有用於不同的域類型的專用標記;例如電子郵件、電話、或者只有整數的域。這種設計擴充了Model-View-Controller 項目的JSP視圖層,並且我可以用它來增強來自邏輯層的映像的分離度。如果標記代碼發生了改變,使用JSP標記校正的Web 設計師和開發人員也不必修改JSP中的任何代碼;而且,他們不必知道它是如何校正的或者他使用了何種Java文法。他們只要把類似HTML的標記包括到他們的JSP頁面中就可以了。【original text】【Download source code】
代碼清單
Listing 1
public int doStartTag() throws JspException {
// retrun code of 1 will cause tag body to execute
if (value == null)
return this.EVAL_BODY_INCLUDE; //check if we have zip code
if (value.equals(null))
//check if value is not null
return this.EVAL_BODY_INCLUDE;
if (value.length() == 5) {
//has to be an integer! short case of zip code xxxxx
try {
Integer.parseInt(value);
return this.SKIP_BODY;
} catch (NumberFormatException e) {
return this.EVAL_BODY_INCLUDE;
}
} else if (value.length() == 10) {
// long case of Zip code xxxxx-xxxx
String part1 = value.substring(0, 5);
String dash = value.substring(5, 6);
String part2 = value.substring(6);
if (!dash.equals("-"))
return this.EVAL_BODY_INCLUDE;
try {
Integer.parseInt(part1);
Integer.parseInt(part2);
return this.SKIP_BODY;
} catch (NumberFormatException e) {
return this.EVAL_BODY_INCLUDE;
}
}
return this.EVAL_BODY_INCLUDE; // all other cases
Listing 2
<FORM action="<%=request.getRequestURI()%>" method="post">
<TABLE border="1">
<input type="hidden" name="ACTIONKEY" value="WeatherAction.viewByZip">
<input type="hidden" name="REDIRECTKEY" value="weather_data">
<input type="hidden" name="validate" value="true">
<TBODY>
<TR>
<TD><font face=Arial size=2>Zip:</font></TD>
<TD>
<% if (validate) { %>
<valTag:notValidZip value="<%=request.getParameter(\"ZIP\")%>">
<!-- do something if field is empty -->
<font color=red face=arial size=2>
Please enter valid Zip Code
</font><br>
<% success=false; %>
</valTag:notValidZip>
<% } %>
<INPUT type="text" name="ZIP"
value="<%=((validate)?request.getParameter("ZIP"):"")%>">
<% if (success && validate) { %>
<jsp:forward page="../MainServlet" />
<% } %>
</TD>
</TR>
<TR>
<TD></TD>
<TD><INPUT type="submit" name="view" value="View"></TD>
</TR>
</TBODY>
</TABLE>
</FORM>
Listing 3
About the Author
Vlad Kofman 是一個從事政府國防合約中的項目的系統構造師。他還參與了主要的華爾街公司和美國政府的企業階層專案。他的主要興趣在於物件導向的程式設計方法和設計模式。