Spring MVC中每個控制器中可以定義多個請求處理方法,我們把這種請求處理方法簡稱爲Action,每個請求處理方法可以有多個不同的參數,以及一個多種類型的返回結果。
一、Action參數類型
1.1、自動參數映射
1.1.1、基本數據類型
方法的參數可以是任意基本數據類型,如果方法參數名與http中請求的參數名稱相同時會進行自動映射,視圖user目錄下的index.jsp與示例代碼如下:
package cn.liuw.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")
public class UserController {
// 自動參數映射
@RequestMapping("/index")
public String index(Model model, int id, String name) {
model.addAttribute("message", "name=" + name + ",id=" + id);
return "user/index";
}
}
<%@ page language="java" contentType="text/html;utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>User</title>
</head>
<body>
${message}
</body>
</html>
運行結果如下:
1.1.2、自定義數據類型
除了基本數據類型,也可以自定義的數據類型,如一個自定義的POJO對象,Spring MVC會通過反射把請中的參數設置到對象中,轉換類型,示例代碼如下:
package cn.liuw.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import cn.liuw.domain.User;
@Controller
@RequestMapping("/user")
public class UserController {
// 自動參數映射自定義數據類型
@RequestMapping("/idx")
public String index(Model model, User user) {
model.addAttribute("message", user);
return "user/index";
}
}
package cn.liuw.domain;
public class User {
private int id;
private String name;
public User() {
}
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
運行結果:
示例中使用的是的URL中的參數,其實也可以是客戶端提交的任意參數,特別是表單中的數據。
1.1.3、複雜數據類型
這裏指的複雜數據類型指的是一個自定義類型中還包含另外一個對象類型,如用戶類型中包含產品對象:
package cn.liuw.domain;
public class User {
private int id;
private String name;
private Product product;
public User() {
}
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Product getProduct() {
return product;
}
public void setProduct(Product product) {
this.product = product;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + "]";
}
}
class Product {
private int pid;
private String pname;
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
@Override
public String toString() {
return "Product [pid=" + pid + ", pname=" + pname + "]";
}
}
複雜數據類型映射示例:
@RequestMapping("/idx2")
public String index2(Model model, User user) {
model.addAttribute("message", user.getName()+","+user.getProduct().getPname());
return "user/index";
}
運行結果:
爲了方便這裏我使用的是url,這裏當然可以是一個表單,如下代碼所示:
<form method="post" action="/user/idx3">
username:<input name="username" /><br/>
pdctname:<input name="product.name" /><br/>
<button>提交</button>
</form>
1.1.4、數組
瀏覽器請求傳遞參數:http://localhost:8080/user/idx01?ids=1&ids=2&ids=3
第一種:
代碼中用數組Integer[] ids接收,如下:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/idx01")
public String index2(Model model, Integer[] ids) {
model.addAttribute("message", Arrays.toString(ids));
return "user/index";
}
}
第二種:
用集合List<Integer> id接收,但是List集合必須得用其他類包裝起來。如下:
包裝類:
public class User {
private int id;
private String name;
List<Integer> ids;
//getter and setter
}
代碼類:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/idx02")
public String index2(Model model, User user) {
model.addAttribute("message", user);
return "user/index";
}
}
以上兩種瀏覽器url傳參一樣,接收不一樣。
1.1.5、List集合類型
不能直接在index2()方法的參數中指定List<T>類型,定義一個類型包裝List集合在其中。這裏Controller中接收代碼與數組類型的第二種方法一樣,但是,傳參數不一樣。
瀏覽器請求傳遞參數:http://localhost:8080/user/idx01?ids[0]=1&ids[1]=2&ids[2]=3
這裏同樣可以使用一個表單向服務器提交數據,不過TomCat高版本可能會產生錯誤。因爲get請求新規範規定不允許url帶特殊字符,解決方法:對請求編碼解碼(UrlDecode、UrlEncode)。
1.1.6、Map集合類型
Map集合與List集合類似,也算封裝在一個類中,請求參數方式也一樣。
1.2、@RequestParam參數綁定
簡單的參數可以使用上一節中講過的自動參數映射,複雜一些的需使用@RequestParam完成,雖然自動參數映射很方便,但有些細節是不能處理的,如參數是否爲必須參數,名稱沒有辦法指定,參數的默認值就沒有有辦法做到了。如果使用@RequestParam可以實現請求參數綁定,Spring MVC會自動查找請求中的參數轉類型並將與參數進行綁定,示例代碼如下:
1.2.1、基本數據類型綁定與註解屬性
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/index")
public String index(Model model, @RequestParam(required = false, defaultValue = "99") int id) {
model.addAttribute("message", id);
return "user/index";
}
}
@RequestParam共有4個註解屬性,required屬性表示是否爲必須,默認值爲true,如果請求中沒有指定的參數會報異常;defaultValue用於設置參數的默認值,如果不指定值則使用默認值,只能是String類型的。name與value互爲別名關係用於指定參數名稱。
運行結果:
1.2.2、List與數組綁定基本數據類型
在上一節中我們使用自動參數映射是不能直接完成List與數組綁定的,結合@RequestParam可以輕鬆實現,示例代碼如下所示:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/index1")
public String index1(Model model, @RequestParam(name = "u") List<String> usernames) {
model.addAttribute("message", usernames.get(0)+","+usernames.get(1));
return "user/index";
}
}
運行結果:
直接在URL中輸入測試數據可以綁定成功,使用表單同樣可行,頁面腳本如下:
user/form.jsp
<%@ page language="java" contentType="text/html;utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>User</title>
</head>
<body>
<form action="/user/index1" method="post">
<p>
<label>愛好:</label>
<input type="checkbox" value="read" name="u" />閱讀
<input type="checkbox" value="online" name="u" />上網
<input type="checkbox" value="game" name="u" />電遊
</p>
<button>提交</button>
</form>
</body>
</html>
user/index.jsp
<%@ page language="java" contentType="text/html;utf-8" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>User</title>
</head>
<body>
${message}
</body>
</html>
請求處理方法代碼如下:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/form")
public String form(){
return "user/form";
}
@RequestMapping("/index1")
public String index1(Model model, @RequestParam(name = "u") List<String> usernames) {
model.addAttribute("message", usernames.get(0)+","+usernames.get(1));
return "user/index";
}
}
運行結果:
@RequestParam("u")是必須的,因爲頁面中的表單name的名稱爲u,所有服務器在收集數據時應該使用u而非usernames,如果同名則可以省去。
1.2.3、@RequestBody
@RequestBody 註解將HTTP請求正文插入方法中,使用適合的 HttpMessageConverter將請求體寫入某個對象。
作用:
1) 該註解用於讀取Request請求的body部分數據,使用系統默認配置的HttpMessageConverter進行解析,然後把相應的數據綁定到要返回的對象上;
2) 再把HttpMessageConverter返回的對象數據綁定到 controller中方法的參數上。
使用時機:
A) GET、POST方式提時, 根據request header Content-Type的值來判斷:
application/x-www-form-urlencoded, 可選(即非必須,因爲這種情況的數據@RequestParam, @ModelAttribute也可以處理,當然@RequestBody也能處理);
multipart/form-data, 不能處理(即使用@RequestBody不能處理這種格式的數據);
其他格式, 必須(其他格式包括application/json, application/xml等。這些格式的數據,必須使用@RequestBody來處理);
B) PUT方式提交時, 根據request header Content-Type的值來判斷:
application/x-www-form-urlencoded, 必須;multipart/form-data, 不能處理;其他格式, 必須;
說明:request的body部分的數據編碼格式由header部分的Content-Type指定;
例如:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
@ResponseBody
//將ajax(datas)發出的請求寫入 User對象中
public User login(@RequestBody User user) {
return user;
}
}
@requestBody註解常用來處理content-type不是默認的application/x-www-form-urlcoded編碼的內容,比如說:application/json或者是application/xml等。一般情況下來說常用其來處理application/json類型。通過@requestBody可以將請求體中的JSON字符串綁定到相應的bean上。
$.ajax({
url:"/login",
type:"POST",
data:'{"userName":"admin","pwd","admin123"}',
content-type:"application/json charset=utf-8",
success:function(data){
alert("request success ! ");
}
});
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/index")
@ResponseBody
public void index(@RequestBody String username,@RequestBody String password) {
System.out.println(username+","+password);
}
}
需要注意的是,JSON字符串中的key必須對應user中的屬性名。
1.2.4、List與數組直接綁定自定義數據類型與AJAX
上一小節中我們綁定的集合中存放的只是基本數據類型,如果需要直接綁定更加複雜的數據類型則需要使用@RequestBody與@ResponseBody註解了,先解釋一下他們的作用:
@RequestBody 將HTTP請求正文轉換爲適合的HttpMessageConverter對象。
@ResponseBody 將內容或對象作爲 HTTP 響應正文返回,並調用適合HttpMessageConverter的Adapter轉換對象,寫入輸出流。
@RequestBody默認接收的Content-Type是application/json,因此發送POST請求時需要設置請求報文頭信息,否則Spring MVC在解析集合請求參數時不會自動的轉換成JSON數據再解析成相應的集合,Spring默認的json協議解析由Jackson完成。要完成這個功能還需要修改配置環境,具體要求如下:
a)、修改Spring MVC配置文件,啓用mvc註解驅動功能,<mvc:annotation-driven />
b)、pom.xml,添加jackson依賴,添加依賴的配置內容如下:
<!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.7.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.7.4</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.7.4</version> </dependency>
c)、ajax請求時需要設置屬性dataType 爲 json,contentType 爲 'application/json;charse=UTF-8',data 轉換成JSON字符串,如果條件不滿足有可能會出現415異常。
示例代碼如下:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/action")
public void action(@RequestBody List<User> users,
HttpServletResponse response) throws IOException{
response.setCharacterEncoding("utf-8");
response.getWriter().write("添加成功!");
}
}
action方法的參數@RequestBody List<User> users是接收從客戶端發送到服務器的User集合。在參數前增加@RequestBody的作用是讓Spring MVC在收到客戶端請求時將選擇合適的轉換器將參數轉換成相應的對象。默認的請求內容並非是application/json,而是application/x-www-form-urlencoded。因此,在客戶端請求時設置內容類型爲application/json。
我們寫一個jsp頁面form1.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>List與數組直接綁定自定義數據類型與AJAX</title>
</head>
<body>
<button type="button" onclick="post_json();">向服務器發送json</button>
<p id="msg"></p>
<script type="text/javascript"
src="<c:url value="/scripts/jQuery1.11.3/jquery-1.11.3.min.js"/>"></script>
<script type="text/javascript">
var users = new Array();
users.push({
id : 1,
name : "zhangsan"
});
users.push({
id : 2,
name : "lisi"
});
users.push({
id : 3,
name : "wangwu"
});
function post_json() {
$.ajax({
type : "POST",
url : "user/action",
data : JSON.stringify(users), //將users對象轉換成json字符串
contentType : "application/json;charset=UTF-8",
//發送信息至服務器時內容編碼類型,(默認: "application/x-www-form-urlencoded")
dataType : "text", //預期服務器返回的數據類型
success : function(result) {
$("#msg").html(result);
}
});
}
</script>
</body>
</html>
頁面中的方法是實現將一個json集合發送到服務器並映射成一個List集合。
1.3、重定向與轉發
重定向:
@Controller
@RequestMapping("/user")
public class UserController {
// 重定向
@RequestMapping("/index")
public String index(Model model) {
return "user/index";
}
@RequestMapping("/login")
public String login(Model model) {
model.addAttribute("message", "login-name");
return "redirect:index";
}
}
轉發:
@Controller
@RequestMapping("/user")
public class UserController {
//轉發
@RequestMapping("/index")
public String index(Model model){
return "user/index";
}
@RequestMapping("/logout")
public String logout(Model model){
model.addAttribute("message","logout-status");
return "forward:index";
}
}
重定向和轉發區別:
1.重定向是客戶端重新發的請求,轉發是服務端的。
2.重定向URL變化,轉發URL不變。
3.我們看到重定向和轉發的運行結果,請求是Request域,重定向只會在第二次請求中攜帶Model中的參數。
二、Action返回值類型