前言: 因为总是需要使用不同的参数传递方式,所以特地来总结一下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. 大家可以数一下,我总共列举了多少种参数传递的情况。

SpringBoot前后端分离参数传递方式总结相关推荐

  1. B站云E办Vue+SpringBoot前后端分离项目——MVC三层架构搭建后台项目

    本项目来源B站云E办,笔记整理了项目搭建的过程和涉及的知识点.对于学习来说,不是复制粘贴代码即可,要知其然知其所以然.希望我的笔记能为大家提供思路,也欢迎各位伙伴的指正. 项目前端学习笔记目录 B站云 ...

  2. Android+SpringBoot前后端分离实现登录注册

    Android+SpringBoot前后端分离实现登录注册 一.登录 1.界面设计 2.Android端 (1)布局文件(activity_login) (2)java文件(LoginActivity ...

  3. springboot前后端分离后权限原理浅谈

    1. 需求描述 最近在梳理springboot前后端分离后的权限管理问题.前段时间,已经把shiro的实现和spring security 的实现进行了初步的了解.如果深入细节,一个篇幅怕是不够.本文 ...

  4. SpringBoot后台管理+Uniapp(混合APP)前端 之 酒店住宿+景点下单管理系统(SpringBoot前后端分离)

    酒店住宿+景点下单管理系统(SpringBoot前后端分离) 之 SpringBoot后台管理+Uniapp(混合APP)前端 SpringBoot前后端分离项目-Thymeleaf模板引擎景区旅游管 ...

  5. 基于SpringBoot前后端分离的众筹系统(附源码)

    基于SpringBoot前后端分离的众筹系统源码下载链接: https://download.csdn.net/download/weixin_47367099/85441573 一.运行步骤 1.环 ...

  6. 新手摸爬滚打:vue+springboot前后端分离项目演示(三)——axios实现前后端交互

    导语:路漫漫其修远兮,吾将上下而求索 前篇: 新手摸爬滚打:vue+springboot前后端分离项目演示(一)--vue cli创建vue2项目 新手摸爬滚打:vue+springboot前后端分离 ...

  7. SpringBoot前后端分离项目中如何制作前端jar包(类似swaggerUI前端jar包制作方法)

    SpringBoot前后端分离项目中如何制作前端jar包(类似swaggerUI前端jar包制作方法) 可用于SpringBoot引用的前端UI的Jar包,类似于SwaggerUI包 WABJAR介绍 ...

  8. 基于vue springboot 前后端分离的电影院会员管理系统

    基于vue springboot 前后端分离的电影院会员管理系统 文章目录 基于vue springboot 前后端分离的电影院会员管理系统 前言 一.主要功能 二.运行截图 1.前端package. ...

  9. springboot前后端分离项目(图书+博客+聊天室)

    一.项目简介 项目名称:blc management system(blc MS) 基于Vue CLI4 + SpringBoot开发的前后端分离项目. 基本功能:对博客和书籍进行增删改查,在聊天室点 ...

最新文章

  1. Laravel 中的异常处理
  2. python3 多线程简介
  3. CUDA和cuDNN到底是啥关系?(cuDNN是基于CUDA的深度学习GPU加速库)
  4. php cap,PHP ImagickDraw setStrokeLineCap()用法及代码示例
  5. java字符连接字符串数组_Java中连接字符串的最佳方法
  6. 开个定时器给echarts组件配置定时更新
  7. Verilog语言实现并行(循环冗余码)CRC校验
  8. postgres 把一个表的值转成另一个表的字段名_用LUT来做一个可动态配置的卷积核...
  9. 编写代码的软件用什么编写的_如果您编写代码,这就是您的黄金时代
  10. Python实现决策树
  11. 单机到集群的WEB架构演变
  12. 创建dqn的深度神经网络_深度Q网络(DQN)-III
  13. illustrator插件开发向导--基础入门(二)--插件定义--PiPL资源--插件管理--插件入口和消息--加载和卸载--启动和关闭
  14. 哪个一键重装电脑系统工具好用些?
  15. mac搭建IPV6网络环境
  16. matlab幼苗识别,基于MATLAB的植物幼苗识别
  17. VOC2007数据集 VOC2012数据集下载 百度云
  18. 树莓派4B连不上wifi的一个意想不到的原因
  19. c语言企业自动化管理系统,基于C语言制作的人事管理系统-自动化毕业论文.doc...
  20. ZigBee——在CC2530的ZStack中添加定时任务

热门文章

  1. 外汇交易中的货币相关性
  2. iOS LaunchScreen设置启动图片 启动页停留时间
  3. L1-028 判断素数 (10 分)(C语言版)
  4. 华为联机对战服务玩家掉线重连案例总结
  5. 修改QQ执行顺序,获取QQ2008最新版密
  6. 【python数据分析】用python进行数据探索1(含各种数据基础分析方法以及直方图、条形图、折线图等基本画法)
  7. 原生APP、WAP 封装的APP、面向API的APP区别与联系
  8. Leetcode 98:验证二叉搜索树
  9. 罗强:腾讯新闻如何处理海量商业化数据?
  10. 华为鸿蒙手机系统什么时候开始更新_华为鸿蒙OS适配计划曝光,一起看看你的手机什么时候可以升级...