第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。(預設值)
正确方式:
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這個接口的實作類裡就有:
特别注意:這種方式和反回字元串方式沒什麼差別,寫法有點不一樣而已。其實傳回字元串這個方式,它的底層最終也會選擇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檔案截圖:(被攔截了)
springmvc.xml中的配置:
<!--告訴前端控制器,哪些靜态資源不攔截-->
<mvc:resources location="/js/" mapping="/js/**"/>
說明:location="/js/" -- js檔案夾下面的任何檔案都可以不攔截
mapping="/js/**" -- 跟你的映射請求是有關的,以後你請求路徑帶/js下面任何檔案都會不會對它進行攔截,如:
在jsp中的請求:
<script src="js/jquery.min.js"></script>
在浏覽器中的表現:
補充其他的:
<!-- 設定靜态資源不過濾 -->
<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);
}
控制台輸出結果:
進一步編寫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提供的。
注:剩餘内容見另一個部落格