1. 程式人生 > >Spring MVC溫故而知新 – 參數綁定、轉發與重定向、異常處理、攔截器

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

單獨 UC exclude require 加載 pre buffered nts 節點

請求參數綁定

當用戶發送請求時,根據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綁定參數

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

@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,defaultvalue

   // 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註解獲取請求頭的信息

  //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請求字段,文件上傳

  @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;
    }

測試:

技術分享圖片

結果:

技術分享圖片

Action返回值

返回ModelAndView

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

  @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的類。兩種方式都是在Handlerde 執行周期內進行攔截操作。

如果要實現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>

全局配置

  <!-- 配置自定義的攔截器 -->
    <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可以使用通配符。

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