SpringBoot前后端分离参数传递方式总结
前言: 因为总是需要使用不同的参数传递方式,所以特地来总结一下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. 大家可以数一下,我总共列举了多少种参数传递的情况。
本文地址:https://blog.csdn.net/qq_40734247/article/details/110151421