Spring MVC溫故而知新 – 參數綁定、轉發與重定向、異常處理、攔截器

請求參數綁定

當用戶發送請求時,根據Spring MVC的請求處理流程,前端控制器會請求處理器映射器返回一個處理器,而後請求處理器適配器之心相應的處理器,此時處理器映射器會調用Spring Mvc 提供的參數綁定組件將請求的key/value 數據綁定到Controller處理器方法對應的形參上。Spring MVC使用Converter轉換器能夠進行各類類型的轉換,也可自定義Converter轉換器,Spring MVC默認轉換器支持的類型有HttpServletRequest、HttpServletResponse、HttpSession、Model、ModelMap。其中Model是一個接口,ModelMap是一個接口實現,做用是將model數據填充到request。前端

簡單類型,自定義類型

 //localhost:8080/springMvcNext/product/infoa?id=1
    @RequestMapping("infoa") public String productInfoa(Model model, Integer id) { model.addAttribute("message", "productid:" + id); return "product/info"; }

備註:若是url中參數名不是id,則不會綁定成功,須要經過使用註解RequestParam綁定參數web

自定義類型傳遞,使用pojo傳遞(Product)spring

@RequestMapping(value="infob",method = RequestMethod.POST) public String productInfob(Model model, Product product) { model.addAttribute("message", "product-price:" + product.getPrice()+"product-name:" + product.getProductName()); return "product/info"; }

使用註解綁定參數

  經過RequestParam註解綁定參數形參名與入參不一致的參數,RerquestParam有三個參數屬性,value參數名,指定要綁定的入參名,required是否必須,默認爲false,defaultValue屬性,用於沒有傳遞時賦默認值。服務器

 //http://localhost:8080/springMvcNext/product/info?productId=1&name=fgsg
    @RequestMapping("info") public String productInfo(Model model, @RequestParam(name = "name", defaultValue = "test") String productName, @RequestParam(required = true) Integer productId) { model.addAttribute("message", "name:" + productName + "  productid:" + productId); return "product/info"; }

經過RequestHeader註解獲取請求頭的信息,RequestHeader一樣有三個參數屬性value,required,defaultvaluecookie

 // http://localhost:8080/springMvcNext/product/info2  // 輸出產品信息:browser:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36 language:zh-CN,zh;q=0.9
    @RequestMapping("info2") public String productInfo2(Model model, @RequestHeader("User-Agent") String browser, @RequestHeader(value = "Accept-Language", required = false, defaultValue = "null") String language) { model.addAttribute("message", "browser:" + browser + "  language:" + language); return "product/info"; }

經過CookieValue註解獲取請求頭的信息mvc

  //http://localhost:8080/springMvcNext/product/info3   //輸出:產品信息:JSESSIONID:0FD3AFA5E445DADACBC1F07568970FEC
  @RequestMapping("info3") public String productInfo3(Model model, @CookieValue("JSESSIONID") String cookie) { model.addAttribute("message", "JSESSIONID:" + cookie); return "product/info"; }

經過HttpServletRequest獲取參數

使用HttpServletRequest獲取請求參數,當客戶端經過HTTP協議訪問服務器時,HTTP請求頭中的全部信息都封裝在這個對象中,開發人員經過這個對象的方法,能夠得到客戶這些信息,HttpServletRequest能夠用於參數解析,Cookie讀取,http請求字段,文件上傳app

  @RequestMapping("info4") public String productInfo(String houseUnitInfo, HttpServletRequest request, HttpServletResponse response) throws IOException { String requestStr = charReader(request); System.out.println(requestStr); return "product/info"; } private String charReader(HttpServletRequest request) throws IOException { BufferedReader br = request.getReader(); String str, wholeStr = ""; while ((str = br.readLine()) != null) { wholeStr += str; } // System.out.println(wholeStr);
        return wholeStr; }

測試:框架

結果:jsp

Action返回值

返回ModelAndView

返回ModelAndView能夠指定視圖名和model數據,ModelAndView提供的addObject方法來給這個模型添加數據,添加的是一個鍵值對的數據post

  @RequestMapping("info5") public ModelAndView productInfo5() { ModelAndView modelAndView = new ModelAndView(); modelAndView.setViewName("product/detail"); modelAndView.addObject("message", "return modelandview"); //modelAndView.addObject("xxx", "yyy");
        return modelAndView; }

返回void,Map,Model

返回void,Map,Model 時,返回對應的邏輯視圖名稱就是請求url,仍然遵循:prefix前綴+視圖名稱 +suffix後綴組成

 //1.返回Map// 訪問視圖: /springMvcNext/WEB-INF/view/product/detail.jsp
    @RequestMapping("detail") public Map<String, Object> detail2313() { Map<String, Object> map = new HashMap<String, Object>(); map.put("message", "product detail"); return map; }  //2.返回void  //返回void時,則響應的視圖頁面對應爲訪問地址 
   //訪問視圖: /springMvcNext/WEB-INF/view/product/info6.jsp @RequestMapping("info6") public void productInfo6() { }

但輸出流中存在輸出內容時,則不會去查找視圖,而是將輸入流中的內容直接響應到客戶端,響應的內容類型是純文本

  @RequestMapping("info7") public void productInfo7(HttpServletResponse response) throws IOException { response.getWriter().write("<h2>void method</h2>");//直接相應結果
 } @RequestMapping("info8") public void productInfo8(HttpServletResponse response) throws IOException { response.sendRedirect("detail");  //重定向 訪問:http://localhost:8080/springMvcNext/product/detail
    }
 //3. 返回Model model對象會用於頁面渲染,視圖路徑使用方法名,與void相似。示例代碼以下:
 @RequestMapping("info9") public Model productInfo9(Model model) { model.addAttribute("message", "product detail"); return model; }

返回String(視圖名)

返回視圖名:Controller類方法返回字符串能夠指定邏輯視圖名,經過視圖解析器解析爲物理視圖地址。

    @RequestMapping("info10") public String productInfo10(Model model) { model.addAttribute("message", "productInfo10"); return "product/detail"; }

Spring MVC轉發與重定向

使用 <mvc:view-controller>標籤轉發

Spring MVC中對與WEB-INF目錄下面的JSP頁面,不能直接經過URL訪問。須要經過轉發的方式,而咱們通常都是在控制器中作轉發映射,對應一些咱們不須要其餘操做的JSP頁面,咱們可使用<mvc:view-controller path=""/>來配置,這樣就能夠不用再控制器中再去作轉發映射。

 <!-- 配置直接進行轉發的頁面,無須進入handler方法 -->
    <mvc:view-controller path="home" />
    <mvc:view-controller path="order/info" />

訪問:http://localhost:8080/springMvcNext/order/info 和 http://localhost:8080/springMvcNext/home 不通過處理器

使用forward或者redirect進行視圖轉發與重定

重定向:Spring mvc中能夠在返回的結果前加上一個前綴「redirect:」,能夠重定向到一個指定的頁面也能夠是另外一個action

轉發:Springmvc中返回結果前加「foword」前綴,注意:轉發是一次請求(相同的request),地址欄的URL不會改變

 //重定向 //訪問:http://localhost:8080/springMvcNext/product/redirecttest 時Url將跳轉http://localhost:8080/springMvcNext/product/info10?redirectparas=test+redirect
    @RequestMapping("redirecttest") public String redirecttest(Model model) { model.addAttribute("redirectparas", "test redirect");  //帶參數跳轉
         return "redirect:/product/info10"; } //轉發 //訪問http://localhost:8080/springMvcNext/product/forwardtest url不會跳轉
    @RequestMapping("forwardtest") public String forwardtest(Model model){ model.addAttribute("forwardparas", "test forward");  //帶參數跳轉
         return "forward:/product/info10"; } 

異常處理

Spring MVC中經過使用@controlleradvice + @ ExceptionHandler 兩個註解能夠實現全局的異常捕捉。

@ExceptionHandler註解的做用是當出現其定義的異常時進行處理的方法,其可使用springmvc提供的數據綁定,好比注入HttpServletRequest等,還能夠接受一個當前拋出的Throwable對象

 

@ControllerAdvice 註解能夠把異常處理器應用到全部控制器 @Controller ,而不是@Controller註解的單個控制器,該異常處理器對當前控制器的全部方法有效;若是單獨某個控制器須要自定義處理異常,不用頂層的異常處理器,能夠在當前控制器內用 @ExceptionHandler 註解 ,這樣當前控制器的異常處理就在當前類中。

備註:使用ControllerAdvice註解類裏面的異常的處理的優先級低於直接定義在處理方法的類中

 實現一個異常處理器:

@ControllerAdvice public class ExceptionHandlers { @ExceptionHandler({ArithmeticException.class}) public ModelAndView toException(Exception e){ ModelAndView mv = new ModelAndView("home"); System.out.println("gobal handler exception"); //雖然不能使用Map往request中存值,可是可使用下面的方法
        mv.addObject("error", e); System.out.println(e); return mv; } }

控制器

@Controller @RequestMapping("exception") public class ExceptionController { // 示例1
        @RequestMapping("test") public ModelAndView test() { System.out.println(10/0); //拋異常
            return new ModelAndView("order/info", "message", "test exception"); } }

攔截器

Spring MVC提供了Interceptor攔截機制,用於請求的預處理和後處理。在Spring MVC中定義一個攔截器有兩種方法:第一種是實現HandlerInterceptor接口,或者繼承實現了HandlerInterceptor接口的類例如(HandlerInterceptorAdapter);第二種方法是實現Spring的WebRequestInterceptor接口(該接口是針對請求的攔截器接口,接口方法參數中沒有response),或者繼承實現了WebrequestInterceptor的類。兩種方式都是在Handler的執行週期內進行攔截操做。

若是要實現HandlerInterceptor接口,須要實現三個方法,preHandle、postHandle、afterCompletion

 preHandle方法在執行Handler方法以前執行,返回false表示攔截請求,不在執行後續邏輯,能夠用來作權限,日誌等。

postHandle方法在執行Handler方法以後,返回modelAndView以前執行,因爲該方法會在DispatcherServlet進行返回視圖渲染以前被調用,因此此方法多被用於同一處理返回視圖,例如將公用的模型數據添加到視圖,或者根據其餘狀況制定公用的視圖。

afterCompletion方法在執行完Handler以後執行,因爲是在Controller方法執行完畢後執行該方法,因此該方法適合進行統一的異常或者日誌處理操做。

實現HandlerInterceptor接口以後須要在Spring的類加載配置文件中配置攔截器實現類,才能使攔截器起到攔截的效果。HandlerInterceptor類加載配置有兩種方式,分別是」針對HandlerMapping配置」和 全局配置。

針對HandlerMapping配置須要在某個處理器映射器配置中將攔截器做爲參數配置進去,以後經過此處理器映射器的handler就會使用配置好的攔截器,配置以下:

<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
        <property name="interceptors">
            <list>
                <ref bean="hInterceptor1" />
                <ref bean="hInterceptor2" />
            </list>
        </property>
        <property name="order" value="1"></property>
    </bean>
    <bean id="hInterceptor1" class="com.sl.interceptors.TestInterceptor"></bean>
    <bean id="hInterceptor2" class="com.sl.interceptors.TestOrderInterceptor"></bean>

全局配置,springmvc框架將配置的全局攔截器注入到每一個HandlerMapping中。

  <!-- 配置自定義的攔截器 -->
    <mvc:interceptors>       
            <bean class="com.sl.interceptors.TestInterceptor"></bean>  
    </mvc:interceptors>

實現一個攔截器:

@Component public class TestInterceptor implements HandlerInterceptor { /** * 當目標方法執行以前,執行此方法,返回false,則再也不執行後續邏輯postHandle、afterCompletion */
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("First preHandle 最早執行.."); return true; } /** * 執行目標方法以後,渲染視圖以前調。 在轉向jsp頁面以前, 能夠對請求域中的屬性,或者視圖進行修改 */
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("First postHandle 執行目標方法以後,渲染視圖以前調。 在轉向jsp頁面以前,"); } /** * 在渲染視圖以後被調用,能夠進行日誌處理 */
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("First afterCompletion 渲染視圖以後調用"); } }

運行controller則能夠看到攔截器執行記錄。

若是定義多個攔截器,則執行順序以下:

  1. preHandle是按配置文件中的順序執行的

  2. postHandle是按配置文件中的倒序執行的

  3. afterCompletion是按配置文件中的倒序執行的

測試驗證:

攔截器的指定範圍:配置攔截器時能夠根據須要制定攔截器做用範圍,針對特定處理器或方法進行攔截。

 <!-- 配置攔截器 -->
    <!-- 使用bean定義一個Interceptor,直接定義在mvc:interceptors根節點下則攔截全部的請求 -->
    <!-- 定義在mvc:interceptor下面的表示是對特定的請求才進行攔截的 -->
<mvc:interceptors>
         <mvc:interceptor> 
        <!-- 指定攔截器做用路徑 --> <mvc:mapping path="/product/*" /> <bean class="com.sl.interceptors.TestInterceptor"></bean> </mvc:interceptor> <mvc:interceptor> <mvc:mapping path="/order/*" /> <bean class="com.sl.interceptors.TestOrderInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>

<mvc:exclude-mapping path=""/>  表示針對該Path不攔截 ,<mvc:mapping path=""/>  表示針對該Path攔截,Path可使用通配符。

相關文章
相關標籤/搜索