天天看點

Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳

第1章 響應資料和結果視圖

1.1 傳回值分類

1.1.1 傳回字元串

什麼情況下用這種方式:(以後的開發基本上都是這樣的開發方式)

開發思路:你發請求,我背景把資料查出來,我存到這個Model對象,它幫我存到request轉發到頁面,前端jsp從request域裡面把值取出來。(以後即可用Model這個對象代替HttpServletRequest對象,達到簡寫的目的,再也不用像HttpServletRequest那麼麻煩了...且使用Servlet原生API會時程式耦合很高,具體原因見:SpringMC 總結 01)

@RequestMapping("/testString")
    public String testString(Model model) {
        System.out.println("execute testString()...");
        //模拟從資料庫中查詢出User對象
        User u = new User();
        u.setUsername("JTL");
        u.setPassword("123");
        u.setAge(17);
        model.addAttribute("user", u);
        return "success";
    }
           

前端界面:通過EL表達式從request域對象中取值

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h3>執行成功</h3>
${user.username}
${user.password}
${user.age}
</body>
</html>
           

1.1.2 傳回值是void

@RequestMapping("/testVoid")
    public void testVoid(Model model) {
        System.out.println("execute testVoid()...");
    }
           

預設情況它會去找這個路徑下的與你請求路徑同名的jsp檔案:這樣非常不好,因為你又的建立一個名為testVoid.jsp。(預設值)

Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳

正确方式:

p.s.差別:

請求轉發:請求轉發是一次請求,不用編寫項目的名稱。

重定向:重定向是兩次請求,需要編寫項目的名稱。

注意:

1、你自己手動去調轉發的方法,它不會再幫你去執行視圖解析器。也就不會自動跳轉到/WEB-INF目錄下(自己配置的)去找jsp。需要你自己提供完整的目錄,不會再使用視圖解析器的對象(/稱為元件)。

2、當你轉發層寫完,如果後面還有代碼的話它會繼續執行,如果你不想讓後面代碼執行,可手動加一個return;

方式1:請求轉發

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        //編寫請求轉發的程式
        req.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(req, resp);
        return;
    }
           

方式2:重定向

1、通過request.getContextPath()擷取到項目名稱

2、重定向等于又發了一個新的請求,你直接發請求是不能直接請求/WEB-INF/pages裡面的頁面,/WEB-INF裡面的東西不能直接請求,轉發是可以的。你隻能請求到/webapp根目錄下的jsp如index.jsp

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        //重定向
        resp.sendRedirect(req.getContextPath()+"/index.jsp");
        return;
    }
           

方式3:直接會進行響應

說明:上面兩種方式都是先跳到某個jsp(跳頁面),最終由Tomcat伺服器幫你生成html,最終幫你響應給使用者。還有這樣一種情況:你可能直接發請求,控制台/控制器直接通過輸出流,把資料響應給浏覽器。response.getWrite()拿到輸出流。

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        // 解決響應中文亂碼
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html;charset=UTF-8");
        //直接會進行響應
        resp.getWriter().print("你好!");
        return;
    }
           

1.1.3 傳回值是ModelAndView對象

說明:由SpringMVC架構提供的一個對象。它也可以通過視圖解析器幫你跳轉到某個頁面。與傳回字元串那個方式的功能是一樣的,查到一個JavaBean放到Model中然後傳回給View視圖;ModelAndView也可以存儲JavaBean對象,也可存儲你想往哪個頁面作 跳轉,與前面那個代碼做的功能是一樣的,隻是寫法有點不一樣。且它底層也會把user對象存入到request域對象中,因為ModelAndView源碼中就有ModelMap這個屬性Model這個接口的實作類裡就有:

Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳

特别注意:這種方式和反回字元串方式沒什麼差別,寫法有點不一樣而已。其實傳回字元串這個方式,它的底層最終也會選擇ModelAndView這個方式!底層源碼還是用的ModelAndView這個類。

@RequestMapping("/testModelAndView")
    public ModelAndView testModelAndView() {
        System.out.println("execute testModelAndView()...");
        //建立ModelAndView對象
        ModelAndView mv = new ModelAndView();
        //模拟從資料庫中查詢出User對象
        User u = new User();
        u.setUsername("JT.L");
        u.setPassword("123");
        u.setAge(17);

        // 把user對象存儲到mv對象中,它底層也會把user對象存入到request域對象中
        mv.addObject("user", u);
        // 跳轉到哪個頁面
        mv.setViewName("success");
        return mv;
    }
           

1.2 轉發和重定向

還有一種寫法(SpringMVC架構提供的轉發和重定向),用一些關鍵字來表示轉發和重定向,這個方式用的比較少,且當用關鍵字去做轉發或重定向時,它是用不了視圖解析器這個對象(元件)的。

1.2.1 forward請求轉發

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");
        
        // 請求的轉發
        return "forward:/WEB-INF/pages/success.jsp";
    }
           

說明:Controller方法在提供了String類型的傳回值之後,預設就是請求轉發。我們也可以像上面例子,使用forward關鍵字顯式進行請求轉發。但是,一旦用了forward:則路徑必須寫成實際視圖url,不能寫邏輯視圖,因為它用不了視圖解析器。它相當于requestl.getRequestDispathcher("url").forward(request,response)。使用請求轉發,既可以轉發到jsp,也可以轉發到其他的控制器方法。

補充,轉發到其他控制器:

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 請求的轉發
        return "forward:testModelAndView";
    }
           

說明:必須加上forward關鍵字,雖然Controller方法在提供了String類型的傳回值之後,預設就是請求轉發,但是你不加forward直接寫其他控制器的RequestMapping如:testModelAndView是轉發不成功的。

1.2.2 Redirect 重定向

說明:使用關鍵字的重定向,不需要像response.sendRedirect(url)方式通過request.getContextPath()加上項目名稱。因為它底層幫你把項目名加上了,以後在用關鍵字作重定向的時候,不用再去加項目的名稱,架構已經預設幫你加好了。

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 重定向
        return "redirect:/index.jsp";
    }
           

重定向也可以定向到其他控制器的方法:

@RequestMapping("/testForwardOrRedirect")    
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 重定向到其他控制器方法
        return "redirect:testModelAndView";
    }
           

總結對比:

1、關鍵字+String傳回類型:請求轉發或重定向到jsp或其他控制器方法的對比

@RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect() {
        System.out.println("execute testForwardOrRedirect()...");

        // 請求的轉發
        // return "forward:/WEB-INF/pages/success.jsp";
        // 請求轉發到其他控制器的方法
        // return "forward:testModelAndView";

        // 重定向
        // return "redirect:/index.jsp";
        // 重定向到其他控制器方法
        return "redirect:testModelAndView";
    }
           

2、傳回值void類型:另一種方式請求轉發或重定向到其他控制器方法:

(一)請求轉發:

@RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        // 請求轉發
        req.getRequestDispatcher("testModelAndView").forward(req,resp);
        return;
    }
           

(二)重定向:

   說明:重定向其他控制器方法也不需要寫項目名稱,但是到其他jsp一定要加上項目名稱:

​
    @RequestMapping("/testVoid")
    public void testVoid(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        System.out.println("execute testVoid()...");
        // 重定向 注意這裡也不需要寫項目名稱
        resp.sendRedirect("testModelAndView");
        return;
    }

​
           

1.3 ResponseBody響應json資料

說明:之前都是用的轉發或者重定向跳轉到jsp再去作響應,可能有這樣一個場景:頁面發送一個ajax請求,明顯是一個異步請求,那我的背景需要把一些對象轉換成json的字元串,然後給你響應回去。如果有這樣一個需求,那麼用ResponseBody這個注解就可以完成這個事。

1、先搭建一個異步的環境。

1)引入jquery.min.js

2)存在的問題:在web.xml中配置了前端控制器,攔截了:/,意思就是任何資源都會被攔截到,在jsp中引入js檔案其實它也會去請求伺服器中對應的那個js檔案,現在就存在這樣一個問題:DispatcherServlet這個前端控制器會把這些靜态的資源檔案給攔截了。解決方式:告訴前端控制器這些靜态資源(靜态資源:js、css、圖檔)不要去攔截;在springmvc.xml檔案中配置:

請求js檔案截圖:(被攔截了)

Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳
Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳

springmvc.xml中的配置:

<!--告訴前端控制器,哪些靜态資源不攔截-->
    <mvc:resources location="/js/" mapping="/js/**"/>
           

說明:location="/js/"  -- js檔案夾下面的任何檔案都可以不攔截

           mapping="/js/**" -- 跟你的映射請求是有關的,以後你請求路徑帶/js下面任何檔案都會不會對它進行攔截,如:

在jsp中的請求:

<script src="js/jquery.min.js"></script>
           

在浏覽器中的表現:

Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳

補充其他的:

<!-- 設定靜态資源不過濾 -->
    <mvc:resources location="/css/" mapping="/css/**"/>  <!-- 樣式 -->
    <mvc:resources location="/images/" mapping="/images/**"/>  <!-- 圖檔 -->
    <mvc:resources location="/js/" mapping="/js/**"/>  <!-- javascript -->
           

特别注意:

jsp中js路徑的兩種寫法:

1)相對路徑方式

<script src="js/jquery.min.js"></script>
           

2)這種寫法記得把isELIgnored="false"設定上,因為用了EL表達式的$符号

<script src="${pageContext.request.contextPath}/js/jquery.min.js">
           

2、編寫ajax請求

<script>
        // 頁面加載,綁定單擊事件
        $(function () {
            //#btn id選擇器
            $("#btn").click(function () {
                // alert("hello btn");
                // 發送ajax請求
                $.ajax({
                    // 編寫json格式,設定屬性和值
                    url:"user/testAjax",
                    //json的maime類型???
                    contentType:"application/json;charset=UTF-8",
                    // ''可以往裡面再做嵌套 json鍵值對
                    data:'{"username":"JTL","password":"123","age":"17"}',
                    dataType:"json",
                    type:"post",
                    success:function (data) {
                        // data伺服器端響應的json資料,進行解析
                        alert(data);// 會彈出一個對象,因為傳回的是一個json的對象
                        // 解析這個對象 取屬性
                        alert(data.username);
                        alert(data.password);
                        alert(data.age);
                    }
                })
            })
        })
    </script>
           

3、把發過來的json封裝到一個JavaBean的對象當中

說明:這個事非常好做,SpringMVC的架構已經幫我們做好了。你發過來的是一串Json的字元串,我能拿到,如果你發的串的key值跟我JavaBean裡面的屬性名是相同的,那麼架構可以幫你把串直接封裝到對象當中。但是在做這個轉換的時候,把這個key封裝到對象的時候,需要用到以下Jar包。這個Jar的作用:把串轉成對象;或者将對象轉換成Json字元串。

<!-- json字元串和JavaBean對象互相轉換的過程中,需要使用jackson的jar包 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>
           

有了以上的Jar包之後,後端是直接能把前端傳過來的Json資料封裝到JavaBean對象當中去,它是一個自動的過程!隻需要你加一個@RequestBody的注解即可。

@RequestMapping("/testAjax")
    public void testAjax(@RequestBody User user) {
        System.out.println("execute testAjax()...");
        // 用戶端發送ajax的請求,傳的是json字元串,後端(自動)把json字元串封裝到user對象中了
        System.out.println(user);
    }
           

控制台輸出結果:

Spring MVC總結 02 第1章 響應資料和結果視圖第2章 SpringMVC實作檔案上傳

進一步編寫Controller方法:

說明:

@RequestBody:接收的

@ResponseBody:響應的,傳回(return user)的時候直接講user對象轉成一個json的字元串,轉完了直接幫你響應

@RequestMapping("/testAjax")
    public @ResponseBody User testAjax(@RequestBody User user) {
        System.out.println("execute testAjax()...");
        // 用戶端發送ajax的請求,傳的是json字元串,後端(自動)把json字元串封裝到user對象中了(你隻需要加一個@RequestBody注解)
        System.out.println(user);
        // 做響應,模拟查詢資料庫
        user.setUsername("JT.L");
        user.setAge(18);
        // 做響應(查出來跟資料庫不一樣) -- MVC架構已經把事全做好了,隻需要傳回一個User對象即可
        // 但是你傳回的是一個對象,但是最終給前端的還是一個json資料,因為設定了傳回的資料類型dataType:json
        // 反對象不行得把對象轉成json,但也不用你自己轉,隻需要加一個@ResponseBody注解
        return user;
    }
           

總結:整個的發送請求獲得響應的過程都完成了,這就是以後用異步發送json資料開發方式。

第2章 SpringMVC實作檔案上傳

2.1 檔案上傳的回顧

2.1.1 檔案上傳的必要前提

1、form表單的enctype取值必須是:multipart/form-data(指定傳輸資料為二進制資料,例如圖檔、mp3、檔案。http請求中的multipart/form-data,會将表單的資料處理為一條消息,以标簽為單元,用分隔符分開。 既可以上傳鍵值對,也可以上傳檔案)

預設值:application/x-www-form-urlencoded(會将表單内的資料轉換為鍵值對)

2、method屬性取值必須是post:如果是get會把你請求的東西放在位址欄上,位址欄的大小是有限制的,裝不了多少資料;大資料的送出必須選post。

3、提供一個檔案選擇域<input type=”file” /> 

2.1.2 檔案上傳的原理分析

當 form 表單的 enctype 取值不是預設值後,request.getParameter()将失效。

enctype=”application/x-www-form-urlencoded”時,form 表單的正文内容是:key=value&key=value&key=value 

當form表單的enctype 取值為Mutilpart/form-data時,請求正文内容就變成:

每一部分都是 MIME 類型描述的正文,如下:

-----------------------------7de1a433602ac   分界符 
Content-Disposition: form-data; name="userName"  協定頭 
  
aaa              協定的正文 
-----------------------------7de1a433602ac 
Content-Disposition: form-data; name="file"; 
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt" 
Content-Type: text/plain         協定的類型(MIME 類型) 
 
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 
-----------------------------7de1a433602ac-- 
           

2.1.3 借助第三方元件實作檔案上傳

這些元件給我們提供了api,可以很方面的解析上傳檔案的請求體,不需要我們過于細節的去解析那個請求體(不需要我們知道請求體的格式也能正确解析)。這個元件是Apache提供的。

注:剩餘内容見另一個部落格

繼續閱讀