Spring MVC 攔截器
一,具體內容:
在所有的開發之中攔截器屬於一個重要的組件,可以說幾乎所有的項目都會提供的概念應用,不管是Spring MVC,還是Struts 2.x都是提供有攔截器的,利用攔截器可以實現更加方便的數據驗證處理。
1,認識攔截器
所謂的攔截器指的是在用戶和具體操作的Action之間做了一個屏障,以保證提交到提交到Action的數據是真實有效的數據;
如果要想實現攔截器的操作處理,那么必須掌握"org.springframework.web.servlet.HandlerInterceptor"該界面的定義,該界面主要有一下幾個處理方法:
如果現在要想從事於具體的驗證處理,那么,就應該在控制層的方法執行前進行驗證,所有使用最多的方法是:preHandle
范例:定義一個攔截器
package cn.wnh.util.validate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class ValidationInterceptor implements HandlerInterceptor {
Logger log = Logger.getLogger(ValidationInterceptor.class) ;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.info(" *** preHandle *** " + handler.getClass());
return true ;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
log.info(" *** postHandle *** " + handler.getClass() + " modelAndView = " + modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object
handler, Exception ex)throws Exception {
log.info(" *** afterCompletion *** " + handler.getClass());
}
}
攔截器定義完成之后實際上是不能直接使用的,你還需要在applicationContext-mvc.xml檔里面設置攔截器的攔截路徑。
修改applicationContext-mvc .xml檔
<mvc:interceptors> <!-- 定義攔截器棧,可以定義多個攔截器-->
<mvc:interceptor> <!--定義某個具體的攔截器 -->
<:mapping path="/pages/**/*.action"/> <!-- action -->
<!-- 定義該攔截器使用的攔截器處理類,必須是界面的子類 -->
<bean class="...validate."/>
</:interceptor>
</:interceptors>
隨后啟動程序,正常執行某個Action。
例如:運行一下此的info方法:
@Controller
public class {
@RequestMapping(value = "/echo/{msgParam}", produces = "text/plain;charset=UTF-8")
public @ResponseBody String echo(@PathVariable("msgParam") String msg) {
return "ECHO : " + msg;
}
// 定義該方法的訪問路徑,而后表示該方法返回的數據類型為普通的文本類型(MIME)
@RequestMapping(value="/info",produces="text/plain;charset=UTF-8")
public @ResponseBody String info() { // 該方法的返回值即回應的主題消息
return "www.mldnjava.cn" ;
}
}
通過執行以上方法發現,此時有一個非常重要的類型出現了:org.springframework.web.method.HandlerMethod,這個類是攔截器中主要使用的一個程序類,此類中主要包含有如下方法:
no |
方法名稱 |
方法類型 |
描述 |
1 |
public Object getBean() |
普通 |
返回觸發此攔截器的程序類(XxxAction) |
2 |
public Member getMethod() |
普通 |
取得具體操作XxxAction中方法的Method對象 |
3 |
public Class<?> getBeanType() |
普通 |
取得觸發此攔截器程序類的Class對象 |
4 |
public MethodParameter[] getMethodeParameters() |
普通 |
取得所有提交到此方法上的參數 |
所有用戶請求提交的參數,在此攔截器路面都使用"org.springframework.core.MethodParameter"程序類進行接收,此類有如下常用方法:、
如果需要通過攔截器進行整體的驗證處理,那么必須要知道一下幾點:
1,用戶提交過類的參數名稱以及參數的內容;
2,下雨具體的驗證規則。
二,服務器端驗證
服務器端的數據的接收必須要保證整個數據的合法性,例如:該接收數字的接收數字,所以按照這樣的原則來講,針對於服務器端的驗證基本上可以確定的是所需要的指定數據類型的驗證,而至於數據是否有效,則屬於另外一個層次的驗證。
-
常用的驗證規則來講,能夠適應的驗證基本上也就四種:
int 型:\d+;
double型:\d+(\.\d+)?;
date 型:\d{4}-\{2}-\d{2}
-
如果要想進行驗證,則肯定不同的A 同中的不同方法可能會使用不同的規則,使用首先奧解決的是該如何去定義這個驗證規則的名字(Validation.properties 文件)。
通過"HandlerMethod"類來取得所需要的一些信息:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
// 需要取得HandlerMethod Action對象,這樣可以取得相關Action信息
HandlerMethod handlerMethod = (HandlerMethod) handler ;
log.info(" *** preHandle *** beanName = " + handlerMethod.getBean().getClass().getSimpleName()
+ " methodName = " + handlerMethod.getMethod().getName());
return true;
}
輸出如下信息:INFO [cn.mldn.util.validate.ValidationInterceptor] - *** preHandle *** beanName = EmpAction methodName= info
所以這個時候對於規則名稱的定義為:EmpAction.info.rules。
在攔截器里面需要拼湊出規則的key的名稱:
通過給定的Action名稱以及啊喲調用的業務方法"rules"一起拼湊出要取出的驗證規則,在Validations.properties中定義如下驗證名稱(Aciton類名稱+該類方法名+rules):
String validationKey = handlerMethod.getBean().getClass().getSimpleName() + "." +handlerMethod.getMethod().getName() + ".rules" ;
所有規則這個屬性資源信息的取出,在所有的Action父類"AbstractAction"定義資源信息的取出操作。所有EmpAction也一定存在這樣的方法。所以在攔截器里面通過具體的對象進行該方法的反射調用;
-
當規則取得完成之后,規則的前面為參數名稱,后面為驗證規則,那么這個時候就可以直接拆分這個驗證規則,隨后進行參數的取得;
取得全部的提交參數,需要針對給定的規則進行拆分控制
String result [] = validationValue.split("\\|") ; //按照|拆分
for (int x = 0 ; x < result.length ; x ++) { // 每一個規則的組成"參數名稱:規則類型"
String temp [] = result[x].split(":") ;
String paramName = temp[0] ;
log.info("提交參數- paramName = " + paramName + " paramValue = " +request.getParameter(paramName));
}
編寫一個具體的數據驗證工具類:ValidatorUtils.java程序類;
package cn.wnh.util.validator;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.springframework.web.method.HandlerMethod;
public class ValidatorUtils {
private static Logger log = Logger.getLogger(ValidatorUtils.class);
/**
*實現提交數據的驗證,使用指定 Action的指定驗證規則處理
* @param request
* @param handlerMethod
* @return 所有的驗證錯誤信息保存在Map集合中返回,如果沒有錯誤,則 Map 集合的長度為0
*/
public static Map<String, String> validate(HttpServletRequest request, HandlerMethod handlerMethod){
// 通過給定的Action名稱以及要調用的業務方法"rules"一起拼湊出要取出的驗證規則,在Validations.properties中定義
String validationKey = handlerMethod.getBean().getClass().getSimpleName() + "."+ handlerMethod.getMethod().getName() + ".rules";
Map<String,String> errors = new HashMap<String,String>() ; //
// log.info(" *** preHandle *** validationValue = " + validationKey);
try {
// key key
AbstractAction.getValue()
Method getValueMethod = handlerMethod.getBean().getClass().getMethod("getValue",
String.class,
Object[].class);
try { // key
// getValue() Method
String validationValue = (String) getValueMethod.invoke(handlerMethod.getBean(),
validationKey, null);
if (validationValue != null) { //
log.info(" *** preHandle *** validationValue = " + validationValue);
//
String result[] = validationValue.split("\\|"); //
for (int x = 0; x < result.length; x++) { // :
String temp[] = result[x].split(":");
String paramName = temp [0];
String paramRule = temp [1] ; //
String paramValue = request.getParameter(paramName) ;
// log.info(" paramName = " + paramName + " paramValue = " +
request.getParameter(paramName));
switch (paramRule) {
case "string" : {
if (!ValidateRuleUtil.isString(paramValue)) { //
String msg = (String)
getValueMethod.invoke(handlerMethod.getBean(), "validation.string.msg", null) ;
errors.put(paramName, msg) ;
}
break ;
}
case "int" : {
if (!ValidateRuleUtil.isInt(paramValue)) { //
String msg = (String)
getValueMethod.invoke(handlerMethod.getBean(), "validation.int.msg", null) ;
errors.put(paramName, msg) ;
}
break ;
}
case "double" : {
if (!ValidateRuleUtil.isDouble(paramValue)) { //
String msg = (String)
getValueMethod.invoke(handlerMethod.getBean(), "validation.double.msg", null) ;
errors.put(paramName, msg) ;
}
break ;
}
case "date" : {
if (!ValidateRuleUtil.isDate(paramValue)) { //
String msg = (String)
getValueMethod.invoke(handlerMethod.getBean(), "validation.date.msg", null) ;
errors.put(paramName, msg) ;
}
break ;
}
case "datetime" : {
if (!ValidateRuleUtil.isDatetime(paramValue)) { //
String msg = (String)
getValueMethod.invoke(handlerMethod.getBean(), "validation.datetime.msg", null) ;
errors.put(paramName, msg) ;
}
break ;
}
case "rand" : {
if (!ValidateRuleUtil.isRand(request,paramValue)) { //
String msg = (String)
getValueMethod.invoke(handlerMethod.getBean(), "validation.rand.msg", null) ;
errors.put(paramName, msg) ;
}
break ;
}
}
}
}
} catch (Exception e) {}
} catch (Exception e) {}
return errors ;
}
}
6,、考慮到要驗證的規則處理標准化,建議可以編寫一個具體的ValidateRuleUtil類完成驗證;
package cn.wnh.util.validator;
/**
*完成的是一個個具體的驗證規則的判斷操作
* @author wnh
*/
public class ValidateRuleUtil {
/**
*判斷是否是整數
* @param str
* @return
*/
public static boolean isInt(String str) {
if (isString(str)) { //驗證數據是否為空
return str.matches("\\d+") ;
}
return false ; // 數據為空返回false
}
/**
* 驗證是否是日期,格式為"yyyy-MM--dd HH:mm:ss"
* @return
*/
public static boolean isDatetime(String str) {
if (isString(str)) {
return str.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}") ;
}
return false ;
}
/**
*驗證是否是日期,格式為"yyyy-MM--dd HH"
* @return
*/
public static boolean isDate(String str) {
if (isString(str)) {
return str.matches("\\d{4}-\\d{2}-\\d{2}") ;
}
return false ;
}
/**
*驗證是否是小數
* @param str
* @return
*/
public static boolean isDouble(String str) {
if (isString(str)) {
return str.matches("\\d+(\\.\\d+)?") ;
}
return false ;
}
/**
* 如果出入的內容為null或者是空字符串,則表示錯誤返回 false
* @param str
* @return
*/
public static boolean isString(String str) {
if (str == null || "".equals(str)) {
return false ;
}
return true ;
}
/**
* 進行驗證碼的檢測,驗證碼的屬性名稱固定為rand
* @param request
* @param param
* @return
*/
public static boolean isRand(HttpServletRequest request,String str) {
if (isString(str)) {
String rand = (String) request.getSession().getAttribute("rand") ;
if (isString(rand)) {
return rand.equalsIgnoreCase(str) ;
}
}
return false ;
}
}
-
定義Messages.properties 文件保存所有的錯誤信息:
validation.string.msg=該數據不允許為空!
validation.int.msg=該數據必須是整數!
validation.double.msg=該數據必須是數字!
validation.rand.msg=該驗證碼輸入有誤!
validation.date.msg= (yyyy-mm-dd)!
validation.datetime.msg= (yyyy-mm-dd hh:mm:ss)!
-
在攔截器里面要根據驗證規則的返回結果進行執行控制;
可以將錯誤頁定義為兩類:
用戶特定的錯誤頁:EmpAction.add.error.page=xxx.jsp;
公共的錯誤頁:error.page=/errors.jsp
error.page=/errors.jsp
EmpAction.add.error.page=/pages/emp_add.jsp
定義消息的讀取類:
package cn.wnh.util.resource;
import java.lang.reflect.Method;
import org.springframework.web.method.HandlerMethod;
public class ResourceReadUtil {
/**
*讀取錯誤頁的配置消息
* @param handlerMethod
* @return
*/
public static String getErrorPageValue(HandlerMethod handlerMethod) {
String pageKey = handlerMethod.getBean().getClass().getSimpleName() + "."
+ handlerMethod.getMethod().getName() + ".error.page";
String pageUrl = getValue(handlerMethod,pageKey) ;
if (pageUrl == null) {
pageUrl = getValue(handlerMethod,"error.page") ;
}
return pageUrl ;
}
/**
*實現消息的手工配置讀取
* @param handlerMethod
* @param msgKey
* @return
*/
public static String getValue(HandlerMethod handlerMethod, String msgKey) {
try {
Method getValueMethod = handlerMethod.getBean().getClass().getMethod("getValue",String.class,Object[].class);
return getValueMethod.invoke(handlerMethod.getBean(), msgKey, null).toString();
} catch (Exception e) {
return null ;
}
}
}
在攔截器里面實現調轉控制:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
boolean flag = true ; //默認放行
// 需要取得HandlerMethod Action對象,這樣可以取得相關的Action消息
HandlerMethod handlerMethod = (HandlerMethod) handler ;
// 表示具體的驗證處理操作,所有的錯誤信息通過Map返回
Map<String,String> errors = ValidatorUtils.validate(request, handlerMethod) ;
if (errors.size() > 0) { //有錯
request.setAttribute("errors", errors); // 保存在Request屬性范圍之中
flag = false ; //表示選擇有錯誤,無法向下執行
request.getRequestDispatcher(ResourceReadUtil.getErrorPageValue(handlerMethod)).forward(request,
response);
} else { //沒有錯誤
}
return flag;
}
以上只能驗證最基礎的表單基本信息驗證。如需要更多的驗證還需要進一步的完善。
二、上傳文件驗證
以上已經能夠實現基本的表單驗證了,這樣的驗證實際上基本上各個的開發結構都可以實現,例如增加寫一個Servlet可以實現,使用Struts 2.x也可以實現。
但是在Spring的攔截器之中,不像Struts 2.x那樣有實現好的攔截器。實際上恰恰是因為這樣才讓整個的開發變得異常的靈活,也就是說可以由用戶增加來做檢測操作。
-
要進行文件上傳的檢測,建議設置名字規則。
在Validations.properties文件里面追加有一下的兩個配置:
#公共的上傳文件驗證規則
mime.rules=image/bmp|image/jpg|image/jpeg|image/png|image/gif
#再編寫一個屬於增加類的驗證規則
EmpAction.add.mime.rules=image/bmp|image/jpg|image/jpeg|image/png|image/gif
-
驗證的所有的處理操作都一個寫在ValidatorUtils 類里面實現具體的上傳文件驗證處理;
if (errors.size() == 0) { //之前沒有錯誤信息,現在表示可以對上傳文件類型進行驗證
//需要判斷是否當前有文件上傳
MultipartResolver mr = new CommonsMultipartResolver() ; //通過它來判斷對於上傳文件的接收操作
if (mr.isMultipart(request)) { //表示的是當前有上傳文件
// 需要拼湊驗證規則使用的key的信息
String mimeKey = handlerMethod.getBean().getClass().getSimpleName() + "."
+ handlerMethod.getMethod().getName() + ".mime.rules" ;
//取得具體的驗證規則信息
String mimeValue = ResourceReadUtil.getValue(handlerMethod, mimeKey) ;
if (mimeValue == null) { //沒有消息讀到,沒有設置單獨的驗證規則
mimeValue = ResourceReadUtil.getValue(handlerMethod, "mime.rules") ;
}
//進行每一個上傳文件袋具體驗證操作
String mimeResult [] = mimeValue.split("\\|") ; //因為是一組規則,需要進行拆分
MultipartRequest mreq = (MultipartRequest) request ; // 處理上傳是的request
Map<String,MultipartFile> fileMap = mreq.getFileMap() ; //取得全部的上傳文件
if (fileMap.size() > 0) { //現在有上傳文件
//需要判斷每一個文件的類型
Iterator<Map.Entry<String,MultipartFile>> iter = fileMap.entrySet().iterator() ;
while (iter.hasNext()) { //判斷每一個文件類型
Map.Entry<String,MultipartFile> me = iter.next() ;
if (me.getValue().getSize() > 0) { //當前的上傳文件長度大於 0,表示有上傳文件
if (!ValidateRuleUtil.isMime(mimeResult,
me.getValue().getContentType())) { //沒有驗證通過
errors.put("file", ResourceReadUtil.getValue(handlerMethod,
"validation.mime.msg")) ;
}
}
}
}
}
}
3、ValidateRuleUtil類中追加是否復合於指定的mime類型的驗證處理
/**
* 驗證傳入的mime類型是否復合於當前的開發要求
* @param mimeRules整體的驗證規則
* @param mime每一個上傳文件的類型
* @return
*/
public static boolean isMime(String mimeRules[], String mime) {
if (isString(mime)) {
for (int x = 0; x < mimeRules.length; x++) {
if (mime.equals(mimeRules[x])) {
return true;
}
}
}
return false;
}
不知道寫得都不夠明白,總的來講完整的基於Spring MVC 設計模式的服務器驗證就實現了。如果有看不明白的人歡迎回復信息,說明自己不明白的地方,本人若有空余時間定會答復!!!
-