前言: 因爲總是需要使用不同的參數傳遞方式,所以特地來總結一下SpringBoot中常用的參數的綁定方式,給有需要的朋友查閱。
SpringBoot參數傳遞
注意:雖然Restful風格很流行,但是大部分還是主要是GET和POST的內容,所以這裏只是列舉GET和POST請求爲例。 而且,無論怎麼樣的花樣傳參,它都是符合上面這個報文結構的!正所謂:萬變不離其宗嘛!
GET請求方式
注意:我這裏是示例形式是:代碼+Postman測試截圖+Fiddler抓包截圖。
01.單個鍵值對參數
/**
* GET 方式傳遞參數 單個參數
* */
@GetMapping("/get01")
public String get01(String comment) {
return comment == null ? "no parameter" : comment;
}
請求不帶參數
請求報文中也沒有數據,響應報文體中有數據
請求帶參數
請求攜帶數據,數據在請求行中,注意數據被編碼了
使用了@RequestParam註解,請求必須攜帶參數,否則就會報錯,否則就是:錯誤碼400 Bad Request
@GetMapping("/get02")
public String get02(@RequestParam("comment") String comment) {
return comment;
}
請求不攜帶參數,請求錯誤 400 Bad Reqeust
請求攜帶參數,接收成功
請求和響應報文
如果參數不添加 @RequestParam 註解,那麼這個參數即可不傳遞,而使用了註解的話,默認是必須傳遞參數的,當然了也可以配置爲false。但是,我傾向於還是顯示使用註解,這樣比較清晰,也可配置,更加靈活。
02.多個鍵值對參數
/**
* GET 方式傳遞參數 多個參數
* */
@GetMapping("/get03")
public String get03(@RequestParam("id") String id,
@RequestParam("name") String name,
@RequestParam("comment") String comment) {
System.out.println(id + " " + name + " " + comment);
return id + " " + name + " " + comment;
}
請求行攜帶多個參數
請求和響應報文
03.鍵值對映射對象
/**
* 使用對象對參數進行封裝,這樣在多個參數時,優勢很明顯。
* 但是這裏無法使用 @RequestParam註解,否則會出錯。
* */
@GetMapping("/get04")
public Comment get04(Comment comment) {
if (Objects.isNull(comment)) {
return null; // 需要對 null 值進行處理
}
System.out.println(comment);
return comment;
}
請求攜帶多個參數,直接映射成對象,但是這裏無法使用@RequestParam,同時也無法使用@RequestBody,因爲參數不在請求體中,至於爲什麼無法使用,暫時還沒明白!
請求和響應報文
因爲沒有使用註解,可以不攜帶參數
04.鍵值對映射Map
/**
* 使用對象封裝參數要求必須具有一個對象,所以可以使用 Map 來封裝,這樣可以減少對象的數
* 量。
* * */
@GetMapping("/get05")
public Map<String, String> get05(@RequestParam Map<String, String> map) {
map.forEach((k, v) -> {
System.out.println(k + " --> " + v);
});
System.out.println(map.size());
return map;
}
多個鍵值對參數
請求和響應報文
05.路徑參數
/**
* 參數和路徑結合,適用於單個參數的情況
* */
@GetMapping("/get06/{id}")
public Comment getById(@PathVariable("id") String id) {
Comment comment = new Comment();
comment.setId(id);
comment.setName("Alfred");
comment.setComment("I love you yesterday and today!");
return comment;
}
請求直接寫在路徑上,成爲路徑的一部分
請求和響應體
注: 請求參數就在路徑上面。
06.返回值爲二進制
前面都是文本數據,現在我們嘗試來獲取二進制數據,注意這個方法需要下面的上傳文件方法向上傳文件,或者你自己在文件夾下面放入一個文件。
/**
* 返回值爲二進制
* 其實這裏可以使用 Files.readAllBytes()這個方法,這樣就簡單了。這裏我就不改了,我習慣了使用這種
* 循環讀取的方式,不過確實有點繁瑣了。
* */
@GetMapping("/get07/{name}")
public void getFile(@PathVariable("name") String name, HttpServletResponse response) {
try (OutputStream out = new BufferedOutputStream(response.getOutputStream())) {
try (InputStream in = new BufferedInputStream(new FileInputStream(new File(baseDir, name)))) {
int len = 0;
byte[] data = new byte[4*1024];
while ((len = in.read(data)) != -1) {
out.write(data, 0, len);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
響應體中含有圖片的數據
請求體報文
響應報文,注意圖片的二進制數據無法解碼,會顯示出亂碼的效果
使用ImageView的方式查看圖片
POST請求方式
01.多個鍵值對參數
/**
* POST方式傳遞參數
* @return
* */
@PostMapping("/post01")
public String post01(@RequestParam("id") String id,
@RequestParam("name") String name,
@RequestParam("comment") String comment) {
System.out.println(id + " " + name + " " + comment);
return id + " " + name + " " + comment;
}
請求體中攜帶鍵值對參數,注意Content-Type類型
請求參數以鍵值對的形式放在請求體中,注意它也是被編碼的
請求體中攜帶鍵值對參數,注意Content-Type類型爲form-data
請求體中的數據以表單數據的形式存放,注意其形式
02.鍵值對映射Map
@PostMapping("/post02")
public Map<String, String> post02(@RequestParam Map<String, String> map) {
map.forEach((k, v) -> {
System.out.println(k + " --> " + v);
});
return map;
}
Content-Type選擇:form-data
Content-Type選擇:x-www-form-urlencoded
03.傳遞json數據映射對象
@PostMapping("/post03")
public Comment post03(@RequestBody Comment comment) {
System.out.println(comment);
return comment;
}
請求參數形式爲json字符串,並且選擇Content-Type選擇 raw,不能選擇其它形式的原因的,form-data和x-www-form-urlencoded都會改變請求參數,通過上面的對比都能看出來了。
請求體中的數據就是原始的傳遞數據,並不會改變
04.json數組映射對象數組
/**
* 傳遞對象數組
* */
@PostMapping("/post04")
public Comment[] post04(@RequestBody Comment[] comments) {
return comments;
}
請求參數爲一個json數組,這個東西以前可是難到我了,我去網上找了一個直接映射成List的寫法,但是後來遇到這個問題我一想,既然單個json是一個對象,那麼json數組,不就是一個對象數組嗎?試了一下,果然是這樣的!
05.json數組映射List
@PostMapping("/post05")
public List<Comment> post05(@RequestBody List<Comment> commentList) {
return commentList;
}
請求參數直接映射成List
06.傳遞二進制數據(文件)
/**
* 傳遞二進制數據
* */
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
try {
file.transferTo(new File(baseDir, fileName)); // 對於 SpringBoot 中使用路徑還是懵逼!
return "success";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return "Fail";
}
以前使用Servlet的時候,上傳文件是很複雜的,後來Servlet3.0中進行了改進,現在框架又進行了進一步的封裝,使用起來更加方便了。
請求報文
8.表單數據(文本+文件)
/**
* 表單數據,含文本和二進制
* */
@PostMapping("/submitInfo01")
public String submitInfo(@RequestParam("id") String id,
@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
System.out.println("id: " + id);
System.out.println("name: " + name);
System.out.println("fileName: " + file != null ? file.getOriginalFilename() : "null");
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
try {
file.transferTo(new File(baseDir, fileName));
return "success";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return "Fail";
}
表單通常是可以攜帶不同的數據,主要是因爲它的形式很適合這樣做,所以可以同時接收文件和文本數據。表單數據使用一個boundary來隔開不同的數據。
09.表單數據,進一步封裝成對象
上面那樣如果表單項比較多的話,映射還是比較麻煩的,可以選擇創建一個對象封裝所有的屬性,這樣處理起來就會更加方便,並且也是面向對象思想的應用。
/**
* 表單數據,含文本和二進制 進一步封裝!
* */
@PostMapping("/submitInfo02")
public String submitInfo02(User user) {
MultipartFile file = user.getFile();
System.out.println("id: " + user.getId());
System.out.println("name: " + user.getName());
System.out.println("fileName: " + user != null ? file.getOriginalFilename() : "null");
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
try {
file.transferTo(new File(baseDir, fileName));
return "success";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return "Fail";
}
10.ajax2.0傳遞二進制數據
/**
* POST以二進制形式傳遞文件,通常的web表單是做不到的,但是ajax2.0以後是支持的,我們來嘗試一下。
* 注意它和 Multipart的區別,Multipart實際上不只包含文件本身的數據,還有文件的其它的信息,例如剛纔獲取的文件名。
* 但是如果以二進制的形式傳遞,它就是完全的文件數據流,不包含任何其它信息,只有文件本身的二進制數據流。
*
* 使用這種形式,只能傳輸單個文件,無法傳輸多個文件,因爲它只是文件本身的二進制數據,如果是多個的話,
* 那麼誰也別想從一個連續的二進制流中把圖片切分出來了。
* */
@PostMapping("/binaryFile")
public String binaryFile(@RequestBody byte[] fileData) {
try {
Files.write(Paths.get(baseDir, UUID.randomUUID().toString() + ".jpg"), fileData);
return "success";
} catch (IOException e) {
e.printStackTrace();
return e.getMessage();
}
}
baseDir路徑下的文件
增補拾遺
GET請求方式,也是可以在映射請求體中的數據的,但是對報文的Content-Type有要求,並且不推薦這樣使用!
/**
* 使用對象對參數進行封裝,這樣在多個參數時,優勢很明顯。
* 但是這裏無法使用 @RequestParam註解,否則會出錯。
* */
@GetMapping("/get04")
public Comment get04(Comment comment) {
if (Objects.isNull(comment)) {
return null; // 需要對 null 值進行處理
}
System.out.println(comment);
return comment;
}
Content-Type: x-www-form-urlencoded
注:無法接收參數
Content-Type: form-data
注:可以接收數據。
/**
* 使用對象封裝參數要求必須具有一個對象,所以可以使用 Map 來封裝,這樣可以減少對象的數
* 量。
* * */
@GetMapping("/get05")
public Map<String, String> get05(@RequestParam Map<String, String> map) {
map.forEach((k, v) -> {
System.out.println(k + " --> " + v);
});
System.out.println(map.size());
return map;
}
GET請求,但是數據在請求體中
這種方式違背了通常的web數據傳輸,因爲我們通常是規定GET方式沒有請求體,POST方式數據在請求體中的。但是GET方式是可以有請求體的,POST方式也可以把參數方式放到請求行中,甚至於GET和POST的請求行和請求體中都可以攜帶數據。但是這樣的話,可就苦了前端了,因爲表單的發送請求的形式基本是固定的,出了ajax可以多一些花樣。所以,如果你是一個GET請求,但是使用@RequestBody來接收參數,這個可就夠前端難受的了。年輕人要講究武德,不要亂用,但是不是不能用,如果不是Web項目,那就可以隨便用了。
@GetMapping("/not_use_like_this")
public Comment not_use_like_this(@RequestBody Comment comment) {
System.out.println(comment);
return comment;
}
全部代碼
目錄結構
package request_learn;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class LearnApplication {
public static void main(String[] args) {
SpringApplication.run(LearnApplication.class, args);
}
}
package request_learn.entity;
import java.io.Serializable;
public class Comment implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
private String comment;
// 省略getter、setter和toString方法
}
package request_learn.entity;
import org.springframework.web.multipart.MultipartFile;
public class User {
private String id;
private String name;
private MultipartFile file;
// 省略getter、setter和toString方法
}
package request_learn.controller;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import request_learn.entity.Comment;
import request_learn.entity.User;
@RestController
@RequestMapping("/test")
public class LearnController {
private static final String baseDir = "D:/test/img/";
/**
* GET 方式傳遞參數 單個參數
* */
@GetMapping("/get01")
public String get01(String comment) {
return comment == null ? "no parameter" : comment;
}
@GetMapping("/get02")
public String get02(@RequestParam("comment") String comment) {
return comment;
}
/**
* GET 方式傳遞參數 多個個參數
* */
@GetMapping("/get03")
public String get03(@RequestParam("id") String id,
@RequestParam("name") String name,
@RequestParam("comment") String comment) {
System.out.println(id + " " + name + " " + comment);
return id + " " + name + " " + comment;
}
/**
* 使用對象對參數進行封裝,這樣在多個參數時,優勢很明顯。
* 但是這裏無法使用 @RequestParam註解,否則會出錯。
* */
@GetMapping("/get04")
public Comment get04(Comment comment) {
if (Objects.isNull(comment)) {
return null; // 需要對 null 值進行處理
}
System.out.println(comment);
return comment;
}
/**
* 使用對象封裝參數要求必須具有一個對象,所以可以使用 Map 來封裝,這樣可以減少對象的數量。
* */
@GetMapping("/get05")
public Map<String, String> get05(@RequestParam Map<String, String> map) {
map.forEach((k, v) -> {
System.out.println(k + " --> " + v);
});
System.out.println(map.size());
return map;
}
/**
* 參數和路徑結合,適用於單個參數的情況
* */
@GetMapping("/get06/{id}")
public Comment getById(@PathVariable("id") String id) {
Comment comment = new Comment();
comment.setId(id);
comment.setName("Alfred");
comment.setComment("I love you yesterday and today!");
return comment;
}
/**
* 返回值爲二進制
* */
@GetMapping("/get07/{name}")
public void getFile(@PathVariable("name") String name, HttpServletResponse response) {
try (OutputStream out = new BufferedOutputStream(response.getOutputStream())) {
try (InputStream in = new BufferedInputStream(new FileInputStream(new File(baseDir, name)))) {
int len = 0;
byte[] data = new byte[4*1024];
while ((len = in.read(data)) != -1) {
out.write(data, 0, len);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* POST方式傳遞參數
* @return
* */
@PostMapping("/post01")
public String post01(@RequestParam("id") String id,
@RequestParam("name") String name,
@RequestParam("comment") String comment) {
System.out.println(id + " " + name + " " + comment);
return id + " " + name + " " + comment;
}
@PostMapping("/post02")
public Map<String, String> post02(@RequestParam Map<String, String> map) {
map.forEach((k, v) -> {
System.out.println(k + " --> " + v);
});
return map;
}
@PostMapping("/post03")
public Comment post03(@RequestBody Comment comment) {
System.out.println(comment);
return comment;
}
/**
* 傳遞二進制數據
* */
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
try {
file.transferTo(new File(baseDir, fileName)); // 對於 SpringBoot 中使用路徑還是懵逼!
return "success";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return "Fail";
}
/**
* 傳遞對象數據
* */
@PostMapping("/post04")
public Comment[] post04(@RequestBody Comment[] comments) {
return comments;
}
@PostMapping("/post05")
public List<Comment> post05(@RequestBody List<Comment> commentList) {
return commentList;
}
/**
* 表單數據,含文本和二進制
* */
@PostMapping("/submitInfo01")
public String submitInfo(@RequestParam("id") String id,
@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
System.out.println("id: " + id);
System.out.println("name: " + name);
System.out.println("fileName: " + file != null ? file.getOriginalFilename() : "null");
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
try {
file.transferTo(new File(baseDir, fileName));
return "success";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return "Fail";
}
/**
* 表單數據,含文本和二進制 進一步封裝!
* */
@PostMapping("/submitInfo02")
public String submitInfo02(User user) {
MultipartFile file = user.getFile();
System.out.println("id: " + user.getId());
System.out.println("name: " + user.getName());
System.out.println("fileName: " + user != null ? file.getOriginalFilename() : "null");
if (!file.isEmpty()) {
String fileName = file.getOriginalFilename();
try {
file.transferTo(new File(baseDir, fileName));
return "success";
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return "Fail";
}
/**
* POST以二進制形式傳遞文件,通常的web表單是做不到的,但是ajax2.0以後是支持的,我們來嘗試一下。
* 注意它和 Multipart的區別,Multipart實際上不只包含文件本身的數據,還有文件的其它的信息,例如剛纔獲取的文件名。
* 但是如果以二進制的形式傳遞,它就是完全的文件數據流,不包含任何其它信息,只有文件本身的二進制數據流。
*
* 使用這種形式,只能傳輸單個文件,無法傳輸多個文件,因爲它只是文件本身的二進制數據,如果是多個的話,
* 那麼誰也別想從一個連續的二進制流中把圖片切分出來了。
* */
@PostMapping("/binaryFile")
public String binaryFile(@RequestBody byte[] fileData) {
try {
Files.write(Paths.get(baseDir, UUID.randomUUID().toString() + ".jpg"), fileData);
return "success";
} catch (IOException e) {
e.printStackTrace();
return e.getMessage();
}
}
@GetMapping("/not_use_like_this")
public Comment not_use_like_this(@RequestBody Comment comment) {
System.out.println(comment);
return comment;
}
}
總結
通過列舉多種參數傳遞的方式,並且實際使用Postman和Fiddler測試,我覺得自己對於大部分的參數傳遞方式都已經很熟練了,這確實是一種很好的學習方式。我本身是屬於視覺學習型的人,所以對我來說學習一樣東西,親眼所見的效果是最好的。大家也要多探索屬於自己的學習方式。
這裏只是一些具體的案例,但是關於@RequestPram和@RequestBody的實際作用,這裏我還不是太瞭解,還需要以後多學習,這裏貼一段它本身的API說明。
In Spring MVC, “request parameters” map to query parameters, form data,
and parts in multipart requests. This is because the Servlet API combines
query parameters and form data into a single map called “parameters”, and
that includes automatic parsing of the request body.
1.query paramters
2.from data
3.parts in multipart
這樣看來 @RequestParam 同時適用於GET和POST方法,但是更加準確的解釋應該是它
可以處理在請求行和請求體中的參數(上面三種情況)。
注意:雖然它適用範圍很廣,但是也不是說可以隨便使用的,也是有限制條件的。
PS:
1. 上面列舉的情況還是不夠,如果有漏網之魚,可以在評論區中提出來,我再增補上去。
2. 大家可以數一下,我總共列舉了多少種參數傳遞的情況。