本博文主要講解的知識點以下:html
在咱們的Struts2中,咱們是繼承ActionSupport來實現校驗的...它有兩種方式來實現校驗的功能前端
而SpringMVC使用JSR-303(javaEE6規範的一部分)校驗規範,springmvc使用的是Hibernate Validator(和Hibernate的ORM無關)java
導入jar包web
配置校驗器spring
<!-- 校驗器 --> <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <!-- 校驗器 --> <property name="providerClass" value="org.hibernate.validator.HibernateValidator" /> <!-- 指定校驗使用的資源文件,若是不指定則默認使用classpath下的ValidationMessages.properties --> <property name="validationMessageSource" ref="messageSource" /> </bean>
錯誤信息的校驗文件配置緩存
<!-- 校驗錯誤信息配置文件 --> <bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> <!-- 資源文件名 --> <property name="basenames"> <list> <value>classpath:CustomValidationMessages</value> </list> </property> <!-- 資源文件編碼格式 --> <property name="fileEncodings" value="utf-8" /> <!-- 對資源文件內容緩存時間,單位秒 --> <property name="cacheSeconds" value="120" /> </bean>
添加到自定義參數綁定的WebBindingInitializer中服務器
<!-- 自定義webBinder --> <bean id="customBinder" class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"> <!-- 配置validator --> <property name="validator" ref="validator" /> </bean>
最終添加到適配器中微信
<!-- 註解適配器 --> <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"> <!-- 在webBindingInitializer中注入自定義屬性編輯器、自定義轉換器 --> <property name="webBindingInitializer" ref="customBinder"></property> </bean>
建立CustomValidationMessages配置文件restful
定義規則session
package entity; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Date; public class Items { private Integer id; //商品名稱的長度請限制在1到30個字符 @Size(min=1,max=30,message="{items.name.length.error}") private String name; private Float price; private String pic; //請輸入商品生產日期 @NotNull(message="{items.createtime.is.notnull}") private Date createtime; private String detail; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name == null ? null : name.trim(); } public Float getPrice() { return price; } public void setPrice(Float price) { this.price = price; } public String getPic() { return pic; } public void setPic(String pic) { this.pic = pic == null ? null : pic.trim(); } public Date getCreatetime() { return createtime; } public void setCreatetime(Date createtime) { this.createtime = createtime; } public String getDetail() { return detail; } public void setDetail(String detail) { this.detail = detail == null ? null : detail.trim(); } }
測試:
<%-- Created by IntelliJ IDEA. User: ozc Date: 2017/8/11 Time: 9:56 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>測試文件上傳</title> </head> <body> <form action="${pageContext.request.contextPath}/validation.action" method="post" > 名稱:<input type="text" name="name"> 日期:<input type="text" name="createtime"> <input type="submit" value="submit"> </form> </body> </html>
Controller須要在校驗的參數上添加@Validation註解...拿到BindingResult對象...
@RequestMapping("/validation") public void validation(@Validated Items items, BindingResult bindingResult) { List<ObjectError> allErrors = bindingResult.getAllErrors(); for (ObjectError allError : allErrors) { System.out.println(allError.getDefaultMessage()); } }
因爲我在測試的時候,已經把日期轉換器關掉了,所以提示了字符串不能轉換成日期,可是名稱的校驗已是出來了...
分組校驗其實就是爲了咱們的校驗更加靈活,有的時候,咱們並不須要把咱們當前配置的屬性都進行校驗,而須要的是當前的方法僅僅校驗某些的屬性。那麼此時,咱們就能夠用到分組校驗了...
步驟:
在咱們以前SSH,使用Struts2的時候也配置過統一處理異常...
當時候是這麼幹的:
詳情可看:blog.csdn.net/hon_3y/arti…
那麼咱們此次的統一處理異常的方案是什麼呢????
咱們知道Java中的異常能夠分爲兩類
對於運行期異常咱們是沒法掌控的,只能經過代碼質量、在系統測試時詳細測試等排除運行時異常
而對於編譯時期的異常,咱們能夠在代碼手動處理異常能夠try/catch捕獲,能夠向上拋出。
咱們能夠換個思路,自定義一個模塊化的異常信息,好比:商品類別的異常
public class CustomException extends Exception { //異常信息 private String message; public CustomException(String message){ super(message); this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
咱們在查看Spring源碼的時候發現:前端控制器DispatcherServlet在進行HandlerMapping、調用HandlerAdapter執行Handler過程當中,若是遇到異常,在系統中自定義統一的異常處理器,寫系統本身的異常處理代碼。。
咱們也能夠學着點,定義一個統一的處理器類來處理異常...
public class CustomExceptionResolver implements HandlerExceptionResolver { //前端控制器DispatcherServlet在進行HandlerMapping、調用HandlerAdapter執行Handler過程當中,若是遇到異常就會執行此方法 //handler最終要執行的Handler,它的真實身份是HandlerMethod //Exception ex就是接收到異常信息 @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { //輸出異常 ex.printStackTrace(); //統一異常處理代碼 //針對系統自定義的CustomException異常,就能夠直接從異常類中獲取異常信息,將異常處理在錯誤頁面展現 //異常信息 String message = null; CustomException customException = null; //若是ex是系統 自定義的異常,直接取出異常信息 if(ex instanceof CustomException){ customException = (CustomException)ex; }else{ //針對非CustomException異常,對這類從新構形成一個CustomException,異常信息爲「未知錯誤」 customException = new CustomException("未知錯誤"); } //錯誤 信息 message = customException.getMessage(); request.setAttribute("message", message); try { //轉向到錯誤 頁面 request.getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(request, response); } catch (ServletException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return new ModelAndView(); } }
<!-- 定義統一異常處理器 --> <bean class="cn.itcast.ssm.exception.CustomExceptionResolver"></bean>
咱們在學習webservice的時候可能就聽過RESTful這麼一個名詞,當時候與SOAP進行對比的...那麼RESTful到底是什麼東東呢???
RESTful(Representational State Transfer)軟件開發理念,RESTful對http進行很是好的詮釋。
若是一個架構支持RESTful,那麼就稱它爲RESTful架構...
如下的文章供咱們瞭解:
www.ruanyifeng.com/blog/2011/0…
綜合上面的解釋,咱們總結一下什麼是RESTful架構:
關於RESTful冪等性的理解:www.oschina.net/translate/p…
簡單來講,若是對象在請求的過程當中會發生變化(以Java爲例子,屬性被修改了),那麼此是非冪等的。屢次重複請求,結果仍是不變的話,那麼就是冪等的。
PUT用於冪等請求,所以在更新的時候把全部的屬性都寫完整,那麼屢次請求後,咱們其餘屬性是不會變的
在上邊的文章中,冪等被翻譯成「狀態統一性」。這就更好地理解了。
其實通常的架構並不能徹底支持RESTful的,所以,只要咱們的系統支持RESTful的某些功能,咱們通常就稱做爲支持RESTful架構...
非RESTful的http的url:http://localhost:8080/items/editItems.action?id=1&....
RESTful的url是簡潔的:http:// localhost:8080/items/editItems/1
從上面咱們能夠發現,url並無.action後綴的,所以咱們要修改核心分配器的配置
<!-- restful的配置 --> <servlet> <servlet-name>springmvc_rest</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 加載springmvc配置 --> <init-param> <param-name>contextConfigLocation</param-name> <!-- 配置文件的地址 若是不配置contextConfigLocation, 默認查找的配置文件名稱classpath下的:servlet名稱+"-serlvet.xml"即:springmvc-serlvet.xml --> <param-value>classpath:spring/springmvc.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>springmvc_rest</servlet-name> <!-- rest方式配置爲/ --> <url-pattern>/</url-pattern> </servlet-mapping>
在Controller上使用PathVariable註解來綁定對應的參數
//根據商品id查看商品信息rest接口 //@RequestMapping中指定restful方式的url中的參數,參數須要用{}包起來 //@PathVariable將url中的{}包起參數和形參進行綁定 @RequestMapping("/viewItems/{id}") public @ResponseBody ItemsCustom viewItems(@PathVariable("id") Integer id) throws Exception{ //調用 service查詢商品信息 ItemsCustom itemsCustom = itemsService.findItemsById(id); return itemsCustom; }
當DispatcherServlet攔截/開頭的全部請求,對靜態資源的訪問就報錯:咱們須要配置對靜態資源的解析
<!-- 靜態資源 解析 --> <mvc:resources location="/js/" mapping="/js/**" /> <mvc:resources location="/img/" mapping="/img/**" />
/**
就表示無論有多少層,都對其進行解析,/*
表明的是當前層的全部資源..
在Struts2中攔截器就是咱們當時的核心,原來在SpringMVC中也是有攔截器的
用戶請求到DispatherServlet中,DispatherServlet調用HandlerMapping查找Handler,HandlerMapping返回一個攔截的鏈兒(多個攔截),springmvc中的攔截器是經過HandlerMapping發起的。
實現攔截器的接口:
public class HandlerInterceptor1 implements HandlerInterceptor { //在執行handler以前來執行的 //用於用戶認證校驗、用戶權限校驗 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("HandlerInterceptor1...preHandle"); //若是返回false表示攔截不繼續執行handler,若是返回true表示放行 return false; } //在執行handler返回modelAndView以前來執行 //若是須要向頁面提供一些公用 的數據或配置一些視圖信息,使用此方法實現 從modelAndView入手 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("HandlerInterceptor1...postHandle"); } //執行handler以後執行此方法 //做系統 統一異常處理,進行方法執行性能監控,在preHandle中設置一個時間點,在afterCompletion設置一個時間,兩個時間點的差就是執行時長 //實現 系統 統一日誌記錄 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("HandlerInterceptor1...afterCompletion"); } }
配置攔截器
<!--攔截器 --> <mvc:interceptors> <!--多個攔截器,順序執行 --> <!-- <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor1"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.HandlerInterceptor2"></bean> </mvc:interceptor> --> <mvc:interceptor> <!-- /**能夠攔截路徑無論多少層 --> <mvc:mapping path="/**" /> <bean class="cn.itcast.ssm.controller.interceptor.LoginInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
若是兩個攔截器都放行
測試結果: HandlerInterceptor1...preHandle HandlerInterceptor2...preHandle HandlerInterceptor2...postHandle HandlerInterceptor1...postHandle HandlerInterceptor2...afterCompletion HandlerInterceptor1...afterCompletion 總結: 執行preHandle是順序執行。 執行postHandle、afterCompletion是倒序執行
1 號放行和2號不放行
測試結果: HandlerInterceptor1...preHandle HandlerInterceptor2...preHandle HandlerInterceptor1...afterCompletion 總結: 若是preHandle不放行,postHandle、afterCompletion都不執行。 只要有一個攔截器不放行,controller不能執行完成
1 號不放行和2號不放行
測試結果: HandlerInterceptor1...preHandle 總結: 只有前邊的攔截器preHandle方法放行,下邊的攔截器的preHandle才執行。
日誌攔截器或異常攔截器要求
攔截器攔截
public class LoginInterceptor implements HandlerInterceptor { //在執行handler以前來執行的 //用於用戶認證校驗、用戶權限校驗 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //獲得請求的url String url = request.getRequestURI(); //判斷是不是公開 地址 //實際開發中須要公開 地址配置在配置文件中 //... if(url.indexOf("login.action")>=0){ //若是是公開 地址則放行 return true; } //判斷用戶身份在session中是否存在 HttpSession session = request.getSession(); String usercode = (String) session.getAttribute("usercode"); //若是用戶身份在session中存在放行 if(usercode!=null){ return true; } //執行到這裏攔截,跳轉到登錄頁面,用戶進行身份認證 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); //若是返回false表示攔截不繼續執行handler,若是返回true表示放行 return false; } //在執行handler返回modelAndView以前來執行 //若是須要向頁面提供一些公用 的數據或配置一些視圖信息,使用此方法實現 從modelAndView入手 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("HandlerInterceptor1...postHandle"); } //執行handler以後執行此方法 //做系統 統一異常處理,進行方法執行性能監控,在preHandle中設置一個時間點,在afterCompletion設置一個時間,兩個時間點的差就是執行時長 //實現 系統 統一日誌記錄 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("HandlerInterceptor1...afterCompletion"); } }
Controller
@Controller public class LoginController { //用戶登錄提交方法 @RequestMapping("/login") public String login(HttpSession session, String usercode,String password)throws Exception{ //調用service校驗用戶帳號和密碼的正確性 //.. //若是service校驗經過,將用戶身份記錄到session session.setAttribute("usercode", usercode); //重定向到商品查詢頁面 return "redirect:/items/queryItems.action"; } //用戶退出 @RequestMapping("/logout") public String logout(HttpSession session)throws Exception{ //session失效 session.invalidate(); //重定向到商品查詢頁面 return "redirect:/items/queryItems.action"; } }
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:Java3y