![](https://cdn.zimug.com/wx-zimug.png)
**注意:本节内容需要结合2.2.2小节的内容联合起来一起看,然后理解**
## 一、HTTP协议的四种传参方式
|HTTP协议组成|协议内容示例|对应Spring注解|
|------|----|-----|
|path info传参|/articles/12 (查询id为12的文章,12是参数)|@PathVariable|
|URL Query String传参|/articles?id=12|@RequestParam|
|Body 传参|Content-Type: multipart/form-data|@RequestParam|
|Body 传参|Content-Type: application/json,或其他自定义格式|@RequestBody|
|Headers 传参|| @RequestHeader|
## 二、常用注解回顾
### 2.1 @RequestBody与@ResponseBody
```
//注意并不要求@RequestBody与@ResponseBody成对使用。
public @ResponseBody AjaxResponse saveArticle(@RequestBody ArticleVO article)
```
如上代码所示:
* @RequestBody修饰请求参数,注解用于接收HTTP的body,默认是使用JSON的格式
* @ResponseBody修饰返回值,注解用于在HTTP的body中携带响应数据,默认是使用JSON的格式。如果不加该注解,spring响应字符串类型,是跳转到模板页面或jsp页面的开发模式。说白了:加上这个注解你开发的是一个数据接口,不加这个注解你开发的是一个页面跳转控制器。
![](https://img.kancloud.cn/a3/e0/a3e03e33a39d83822bb56356bc087116_1207x685.png)
在使用@ResponseBody注解之后程序不会再走视图解析器,也就不再做html视图渲染,而是直接将对象以数据的形式(默认JSON)返回给请求发送者。那么我们有一个问题:如果我们想接收或XML数据该怎么办?我们想响应excel的数据格式该怎么办?我们后文来回答这个问题。
### 2.2. @RequestMapping注解
@RequestMapping注解是所有常用注解中,最有看点的一个注解,用于标注HTTP服务端点。它的很多属性对于丰富我们的应用开发方式方法,都有很重要的作用。如:
* value: 应用请求端点,最核心的属性,用于标志请求处理方法的唯一性;
* method: HTTP协议的method类型, 如:GET、POST、PUT、DELETE等;
* consumes: HTTP协议请求内容的数据类型(Content-Type),例如application/json, text/html;
* produces: HTTP协议响应内容的数据类型。下文会详细讲解。
* params: HTTP请求中必须包含某些参数值的时候,才允许被注解标注的方法处理请求。
* headers: HTTP请求中必须包含某些指定的header值,才允许被注解标注的方法处理请求。
```
@RequestMapping(value = "/article", method = POST)
@PostMapping(value = "/article")
```
上面代码中两种写法起到的是一样的效果,也就是PostMapping等同于@RequestMapping的method等于POST。同理:@GetMapping、@PutMapping、@DeleteMapping也都是简写的方式。
### 2.3. @RestController与@Controller
@Controller注解是开发中最常使用的注解,它的作用有两层含义:
* 一是告诉Spring,被该注解标注的类是一个Spring的Bean,需要被注入到Spring的上下文环境中。
* 二是该类里面所有被RequestMapping标注的注解都是HTTP服务端点。
@RestController相当于 @Controller和@ResponseBody结合。它有两层含义:
* 一是作为Controller的作用,将控制器类注入到Spring上下文环境,该类RequestMapping标注方法为HTTP服务端点。
* 二是作为ResponseBody的作用,请求响应默认使用的序列化方式是JSON,而不是跳转到jsp或模板页面。
### 2.4. @PathVariable 与@RequestParam
PathVariable用于URI上的{参数},如下方法用于删除一篇文章,其中id为文章id。如:我们的请求URL为“/article/1”,那么将匹配DeleteMapping并且PathVariable接收参数id=1。而RequestParam用于接收普通表单方式或者ajax模拟表单提交的参数数据。
```
@DeleteMapping("/article/{id}")
public @ResponseBody AjaxResponse deleteArticle(@PathVariable Long id) {
@PostMapping("/article")
public @ResponseBody AjaxResponse deleteArticle(@RequestParam Long id) {
```
## 二、接收复杂嵌套对象参数
有一些朋友可能还无法理解RequestBody注解存在的真正意义,表单数据提交用RequestParam就好了,为什么还要搞出来一个RequestBody注解呢?RequestBody注解的真正意义在于能够使用对象或者嵌套对象接收前端数据。
![](https://img.kancloud.cn/49/7a/497af21eb950a0fc366ee4c388dcb5d4_699x244.png)
仔细看上面的代码,是一个paramData对象里面包含了一个bestFriend对象。这种数据结构使用RequestParam就无法接收了,RequestParam只能接收平面的、一对一的参数。像上文中这种数据结构的参数,就需要我们在java服务端定义两个类,一个类是ParamData,一个类是BestFriend.
```
public class ParamData {
private String name;
private int id;
private String phone;
private BestFriend bestFriend;
public static class BestFriend {
private String address;
private String sex;
}
}
```
* 注意上面代码中省略了GET、SET方法等必要的java plain model元素。
* 注意成员变量名称一定要和JSON属性名称对应上。
* 注意接收不同类型的参数,使用不同的成员变量类型
完成以上动作,我们就可以使用`@RequestBody ParamData paramData`,一次性的接收以上所有的复杂嵌套对象参数了,参数对象的所有属性都将被赋值。
## 三、Http数据转换的原理
大家现在使用JSON都比较普遍了,其方便易用、表达能力强,是绝大部分数据接口式应用的首选。那么如何响应其他的类型的数据?其中的判别原理又是什么?下面就来给大家介绍一下:
![](https://img.kancloud.cn/b5/26/b52618ad06c11a5bb513244d6e77aa36_846x289.png)
* 当一个HTTP请求到达时是一个InputStream,通过HttpMessageConverter转换为java对象,从而进行参数接收。
* 当对一个HTTP请求进行响应时,我们首先输出的是一个java对象,然后由HttpMessageConverter转换为OutputStream输出。
当我们在Spring Boot应用中集成了jackson的类库之后,如下的一些HttpMessageConverter将会被加载。
|实现类|功能说明|
|---|---|
|StringHttpMessageConverter|将请求信息转为字符串|
|FormHttpMessageConverter|将表单数据读取到MultiValueMap中|
|XmlAwareFormHttpMessageConverter|扩展与FormHttpMessageConverter,如果部分表单属性是XML数据,可用该转换器进行读取|
|ResourceHttpMessageConverter|读写org.springframework.core.io.Resource对象|
|BufferedImageHttpMessageConverter|读写BufferedImage对象|
|ByteArrayHttpMessageConverter|读写二进制数据|
|SourceHttpMessageConverter|读写java.xml.transform.Source类型的对象|
|MarshallingHttpMessageConverter|通过Spring的org.springframework,xml.Marshaller和Unmarshaller读写XML消息|
|Jaxb2RootElementHttpMessageConverter|通过JAXB2读写XML消息,将请求消息转换为标注的XmlRootElement和XmlType连接的类中|
|MappingJacksonHttpMessageConverter|利用Jackson开源包的ObjectMapper读写JSON数据|
|RssChannelHttpMessageConverter|读写RSS种子消息|
|AtomFeedHttpMessageConverter|和RssChannelHttpMessageConverter能够读写RSS种子消息|
根据HTTP协议的Accept和Content-Type属性,以及参数数据类型来判别使用哪一种HttpMessageConverter。当使用RequestBody或ResponseBody时,再结合前端发送的Accept数据类型,会自动判定优先使用MappingJacksonHttpMessageConverter作为数据转换器。但是,不仅JSON可以表达对象数据类型,XML也可以。如果我们希望使用XML格式该怎么告知Spring呢,那就要使用到produces属性了。
```
@GetMapping(value ="/demo",produces = MediaType.APPLICATION_XML_VALUE)
```
这里我们明确的告知了返回的数据类型是xml,就会使用Jaxb2RootElementHttpMessageConverter作为默认的数据转换器。当然实现XML数据响应比JSON还会更复杂一些,还需要结合@XmlRootElement、@XmlElement等注解实体类来使用。同理consumes属性你是不是也会用了呢。
## 四、自定义HttpMessageConverter
其实绝大多数的数据格式都不需要我们自定义HttpMessageConverter,都有第三方类库可以帮助我们实现(包括下文代码中的Excel格式)。但有的时候,有些数据的输出格式并没有类似于Jackson这种类库帮助我们处理,需要我们自定义数据格式。该怎么做?
下面我们就以Excel数据格式为例,写一个自定义的HTTP类型转换器。实现的效果就是,当我们返回AjaxResponse这种数据类型的话,就自动将AjaxResponse转成Excel数据响应给客户端。
~~~
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
~~~
```
@Service
public class ResponseToXlsConverter extends AbstractHttpMessageConverter<AjaxResponse> {
private static final MediaType EXCEL_TYPE = MediaType.valueOf("application/vnd.ms-excel");
ResponseToXlsConverter() {
super(EXCEL_TYPE);
}
@Override
protected AjaxResponse readInternal(final Class<? extends AjaxResponse> clazz,
final HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return null;
}
//针对AjaxResponse类型返回值,使用下面的writeInternal方法进行消息类型转换
@Override
protected boolean supports(final Class<?> clazz) {
return (AjaxResponse.class == clazz);
}
@Override
protected void writeInternal(final AjaxResponse ajaxResponse, final HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotWritableException {
final Workbook workbook = new HSSFWorkbook();
final Sheet sheet = workbook.createSheet();
final Row row = sheet.createRow(0);
row.createCell(0).setCellValue(ajaxResponse.getMessage());
row.createCell(1).setCellValue(ajaxResponse.getData().toString());
workbook.write(outputMessage.getBody());
}
}
```
* 实现AbstractHttpMessageConverter接口
* 指定该转换器是针对哪种数据格式的?如上文代码中的"application/vnd.ms-excel"
* 指定该转换器针对那些对象数据类型?如上文代码中的supports函数
* 使用writeInternal对数据进行输出处理,上例中是输出为Excel格式。
![](https://img.kancloud.cn/5f/75/5f751a7fe484527316899ce811d7af3e_842x132.png)
- 核心内容介绍
- 问题Q&A汇总
- 本文档已达38万字
- 联系作者-代码工具获取
- 作者的其他作品推荐
- vue深入浅出系列
- Spring Security-JWT-OAuth2一本通
- 实战前后端分离RBAC权限管理系统
- 实战SpringCloud微服务从青铜到王者
- 第一章 spring boot 2.x基础及概念入门
- 1.1.spring boot 产生的背景及其优势
- 1.2.helloworld及项目结构介绍
- 1.3.需要先了解的核心概念
- 1.4.提高开发效率必备工具lombok
- 1.5.IDEA环境下的热加载与热部署
- 1.6.开发过程中常用IDEA插件
- 第二章 RESTful接口实现与测试
- 2.1.RESTful接口与http协议状态表述
- 2.2.1.Spring常用注解及基础讲解
- 2.2.2.常用注解开发一个RESTful接口
- 2.2.3.配合前端axios传参总结
- 2.3.1 JSON数据处理与PostMan测试
- 2.3.2. Jackson全局配置
- 2.4.使用Mockito编码完成接口测试
- 2.5. 使用Swagger2构建API文档
- 2.6.接口文档多种格式导出离线阅读
- 2.7.Swagger3-即OpenAPI使用
- 第三章 spring boot 配置管理
- 3.1.结合源码讲解bean自动装配原理
- 3.2.详解YAML语法及占位符语法
- 3.3.YAML配置绑定变量两种方式
- 3.4.配置属性值数据绑定校验
- 3.5.加载额外配置文件的两种方式
- 3.6.使用SpEL表达式绑定配置项
- 3.7.profile不同环境使用不同配置
- 3.8.配置及配置文件的加载优先级
- 3.9.配置文件敏感字段加密
- 第四章 整合数据库开发框架
- 4.1.整合Spring JDBC操作数据
- 4.2 Spring JDBC多数据源的实现
- 4.3.Spring JDBC JTA实现分布式事务
- 4.4.主流ORM持久层框架选型
- 4.5.java bean的赋值转换
- 4.6.整合Spring Data JPA操作数据
- 4.6.1.SpringDataJPA实现分页排序
- 4.7.Spring data JPA的多数据源实现
- 4.8.JPA+atomikos实现分布式事务
- 4.9.1.整合MybatisGenerator操作数据
- 4.9.2.整合mybatisPlus操作数据库
- 4.10.Mybatis开发最佳实践总结
- 4.11.Spring mybatis的多数据源实现
- 4.12.1.mybatis+atomikos实现分布式事务
- 4.12.2.mybatisplus+atomikos实现分布式事务
- 4.13.Spring事务与分布式事务
- 4.14.一行代码实现RESTFul接口
- 4.15.一键生成数据库文档
- 第五章 整合静态资源与模板引擎
- 5.1.1.webjars与静态资源
- 5.1.2.访问jar内部静态资源
- 5.2.模板引擎选型与未来趋势
- 5.3.web应用开发之整合jsp
- 5.4.web应用开发之整合freemarker
- 5.5.web应用开发之整合thymeleaf
- 5.6.thymeleaf基础语法讲解
- 5.7.thymeleaf内置对象与工具类
- 5.8.公共片段(标签)与内联js
- 第六章 生命周期内的拦截过滤与监听
- 6.1.Servlet域对象与属性变化监听
- 6.2.Servlet过滤器的实现
- 6.3.spring拦截器及请求链路说明
- 6.4.自定义事件的发布与监听
- 6.5.应用启动的监听
- 6.6.类初始化监听
- 第七章 嵌入式容器的配置与应用
- 7.1.嵌入式容器的运行参数配置
- 7.2.为Web容器配置HTTPS
- 7.3.切换到jetty&undertow容器
- 7.4.打war包部署到外置tomcat容器
- 第八章 统一全局异常处理
- 8.1.设计一个优秀的异常处理机制
- 8.2.自定义异常和相关数据结构
- 8.3.通用全局异常处理逻辑
- 8.4.服务端数据校验异常处理逻辑
- 8.5.AOP完美处理页面跳转异常
- 第九章 日志框架与全局日志管理
- 9.1.日志框架的体系结构
- 9.2.logback日志框架配置
- 9.3.1.log4j2日志框架整合与使用
- 9.3.2.log4j2异步日志配置
- 9.4.拦截器实现统一访问日志
- 第十章 异步任务与定时任务
- 10.1.实现Async异步任务
- 10.2.为异步任务规划线程池
- 10.3.通过@Scheduled实现定时任务
- 10.4.quartz简单定时任务(内存持久化)
- 10.5.quartz动态定时任务(数据库持久化)
- 10.6.可观测异步任务线程池
- 第十一章 redis缓存与session共享
- 11.1.使用docker安装redis
- 11.2.redis数据结构与应用场景
- 11.3.单例哨兵及集群模式整合
- 11.4.使用redisTemplate操作数据
- 11.5.使用Redis Repository操作数据
- 11.6.spring cache缓存基本用法
- 11.7.1.详述缓存声明式注解的使用
- 11.7.2.EhCache缓存
- 11.8.缓存雪崩穿透等解决方案
- 11.9.集群多节点应用session共享
- 11.10.介绍redis分布式锁
- 11.11.RedisLockRegistry分布式锁
- 11.12.使用redisson实现分布式锁
- 第十二章 整合分布式文件系统
- 12.1.文件本地上传与提供访问服务
- 12.2.MinIO简介与选型介绍
- 12.3.MinIO的安装与基础用法
- 12.4.整合MinIO的JavaSDK
- 12.5.fastdfs简介及架构说明
- 12.6.使用docker安装fastdfs
- 12.7.开发一个自定义fastdfs-starter
- 12.8.整合fastdfs操作文件数据
- 第十三章 服务器推送技术
- 13.1.主流服务器推送技术说明
- 13.2.服务端推送事件SSE
- 13.3.双向实时通信websocket
- 第十四章 邮件发送的整合与使用
- 14.1.基础协议及邮件配置整合
- 14.2.发送html和基于模板的邮件
- 14.3.发送带附件和内联附件邮件
- 第十五章 应用程序监控管理
- 15.1.Actuator应用监控快速入门
- 15.2.Actuator服务保护缓存及跨域
- 15.3.SpringBootAdmin界面化监控
- 第十六章 消息队列的整合与使用
- 16.1.消息队列与JMS规范简介
- 16.2.使用docker安装activeMQ
- 16.3.activeMQ实现点对点队列
- 16.4.activeMQ实现发布订阅队列
- 16.5.docker安装RocketMQ
- 16.6.RocketMQ实现2种消费模式
- WebFlux文档迁移的说明
- 番外篇:周边技术生态
- centos7安装docker图文详解
- docker安装mongodb(单点)图文详解
- 整合Spring data mongodb操作数据
- windows下如何安装git
- springboot非web项目构建
- 多种方式shutdown应用服务
- SpringBoot-2.2版本的变化
- SpringBoot中如何使用Cookies
- 使用flyway管理数据库版本
- SpringBoot解决跨域访问的问题
- IDEA远程调试linux上的SB项目
- 附录一:mybatis-plus
- f1-1.CURD快速入门
- f1-2.条件构造器使用与总结
- f1-3.自定义SQL
- f1-4.表格分页与下拉分页查询
- f1-5.ActiveRecord模式
- f1-6.主键生成策略
- f1-7.MybatisPlus代码生成器
- f1-8.逻辑删除
- f1-9.字段自动填充
- f1-10.多租户解决方案
- f1-11 日志打印输出
- 附录二:RestTemplate
- f2-1.基本介绍及配置使用
- f2-2.底层HTTP客户端库的切换
- f2-3.GET请求使用详解
- f2-4.POST请求使用详解
- f2-5.HTTP method使用方法详解
- f2-6.文件上传与下载
- f2-7.请求失败异常处理
- f2-8.自动重试机制
- f2-9.通过BasicAuth认证
- f2-10使用代理作为跳板