目录
引言
统一数据格式
实例理解
特殊 String 类型处理
实例理解
分析返回的流程
补充知识
分析报错原因
解决方案一
解决方案二
最终测试
引言
- 统一数据格式能 方便前端程序员更好的接收和解析后端返回的数据
- 统一数据格式能 降低约定前后端交互接口的成本,因为所有接口都是这样返回的
- 统一数据格式 有利于项目统一数据的维护和修改
统一数据格式
- 两步实现统一数据格式返回
实例理解
- 创建一个 ResponseAdvice 类,并添加 @ControllerAdvice 注解
package com.example.demo.component; import org.springframework.web.bind.annotation.ControllerAdvice; @ControllerAdvice public class ResponseAdvice { }
- 实现 ResponseBodyAdvice 接口,重写 supports 和 beforeBodyWrite 方法
package com.example.demo.component; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import java.util.HashMap; @ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { /* * supports 方法默认返回 false,即默认不执行 beforeBodyWrite 方法 * 将 false 改为 true 意味着将会执行 beforeBodyWrite 方法 * */ @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 此处判断传来的 body 为 HashMap 类型时,将直接返回 if(body instanceof HashMap) { return body; } // 到这里表示 body 不为 HashMap 类型,需要统一数据返回格式 HashMap<String,Object> result = new HashMap<>(); result.put("code",200); result.put("msg",""); result.put("data",body); return result; } }
- 其中的 beforeBodyWrite 方法用来实现统一数据返回
- 我们再创建一个 UserController 类,并编写两个 test1 和 test2 方法用来测试
package com.example.demo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @RestController @RequestMapping("/user") public class UserController { @RequestMapping("/test1") public int test1() { return 1; } @RequestMapping("/test2") public HashMap<String,Object> test2() { HashMap<String,Object> result = new HashMap<>(); result.put("code",200); result.put("msg",""); result.put("data",2); return result; } }
- 在浏览器中输入对应的 URL,来访问调用 UserController 类中的 test1 和 test2 方法
- 此时成功实现相同数据格式返回
注意:
- 该写法其实相当于设定了一个保底的返回格式,属于保底策略
- 当返回的数据格式不是约定好的数据格式时,上述方法就会帮我们将其修改为按照约定好的数据格式在进行返回给前端
特殊 String 类型处理
实例理解
- 我们创建一个 UserController 类,并编写 test3 方法用来测试上述的统一数据格式返回
- 此处我们将返回一个字符串 "hello!",并希望其帮我们修改为标准的数据返回格式
package com.example.demo.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @RestController @RequestMapping("/user") public class UserController { @RequestMapping("/test3") public String test3() { return "hello!"; } }
- 在浏览器中输入对应的 URL,来访问调用 UserController 类中的 test3 方法
- 此处报错信息为 试图将 HashMap 类型强制转化成 String 类型
分析返回的流程
- test3 方法准备返回 String 类型,返回前需要进行类型的判断,如果为 HashMap 类型则 直接返回
- 经判定此处不为 HashMap 类型,则统一返回数据格式 ——> 将 String 类型转换为 HashMap 类型
- 最后再将 HashMap 转换为 application/json 字符串给前端
补充知识
- 在执行第三步时,Spring 框架会根据返回的类型进行判断,选择不同的转换器进行类型转换
分析报错原因
- 正是因为 test3 方法的返回类型为 String 类型
- 所以此时 HashMap 类型的数据 将会使用 StringHttpMessageConverter 转换器 来将其转化为 json 格式的字符串
- 然而 StringHttpMessageConverter 转换器是专门用于处理 String 类型数据的
- 这样就导致了 异常的发生,即在使用 StringHttpMessageConverter 转换器的前提下试图将 HashMap 对象强制转化成 String 类型(json格式的字符串为 String 类型)
解决方案一
- 直接将 StringHttpMessageConverter 移除
package com.example.demo.config; import com.example.demo.component.LoginInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class MyConfig implements WebMvcConfigurer { @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { converters.removeIf(converter -> converter instanceof StringHttpMessageConverter); } }
解决方案二
- 在统一数据返回格式时,单独处理 String 类型,让其返回一个 String 字符串,而非 HashMap
package com.example.demo.component; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import java.util.HashMap; @ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Autowired private ObjectMapper objectMapper; /* * supports 方法默认返回 false,即默认不执行 beforeBodyWrite 方法 * 将 false 改为 true 意味着将会执行 beforeBodyWrite 方法 * */ @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 此处判断传来的 body 为 HashMap 类型时,将直接返回 if(body instanceof HashMap) { return body; } // 到这里表示 body 不为 HashMap 类型,需要统一数据返回格式 HashMap<String,Object> result = new HashMap<>(); result.put("code",200); result.put("msg",""); result.put("data",body); // 判断 body 的类型是否为 String 类型 if(body instanceof String) { try { // 该方法可以将 HashMap 或 Java 对象转换为 json 格式的字符串 return objectMapper.writeValueAsString(result); } catch (JsonProcessingException e) { e.printStackTrace(); } } return result; } }
最终测试
- 此处我们任选一种解决方案 进行代码的修改和添加
- 并在浏览器中输入对应的 URL 对 test3 方法进行访问调用