SpringBoot统一功能处理——统一数据返回格式

发布于:2024-08-10 ⋅ 阅读:(38) ⋅ 点赞:(0)

目录

一、简单使用

二、存在的问题描述

三、优点


一、简单使用

统一的数据返回格式使用  @ControllerAdvice ResponseBodyAdvice 的方式实现 @ControllerAdvice 表示控制器通知类。
添加类 ResponseAdvice , 实现 ResponseBodyAdvice 接口,并在类上添加 @ControllerAdvice 注解。
import com.example.demo.model.Result;
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;
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
 @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) {
 return Result.success(body);
 }
}
supports方法: 判断是否要执行beforeBodyWrite方法。true为执行,false不执行。 通过该方法可以选择哪些类或哪些方法的response要进行处理, 其他的不进行处理。
从returnType获取类名和方法名:
//获取执行的类
Class<?> declaringClass = returnType.getMethod().getDeclaringClass();
//获取执行的方法
Method method = returnType.getMethod();
beforeBodyWrite方法: 对response方法进行具体操作处理。

二、存在的问题描述

SpringMVC默认会注册一些自带的 HttpMessageConverter (从先后顺序排列分别为):
  • ByteArrayHttpMessageConverter 
  • StringHttpMessageConverter 
  • SourceHttpMessageConverter 
  • SourceHttpMessageConverter 
  • AllEncompassingFormHttpMessageConverter
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
 implements BeanFactoryAware, InitializingBean {
 
 //...
 public RequestMappingHandlerAdapter() {
 this.messageConverters = new ArrayList<>(4);
 this.messageConverters.add(new ByteArrayHttpMessageConverter());
 this.messageConverters.add(new StringHttpMessageConverter());
 if (!shouldIgnoreXml) {
 try {
 this.messageConverters.add(new SourceHttpMessageConverter<>());
 }
 catch (Error err) {
 // Ignore when no TransformerFactory implementation is available
   }
 }
    this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
 }
 //...
}
其中AllEncompassingFormHttpMessageConverter 会根据项目依赖情况添加对应的HttpMessageConverter:
public AllEncompassingFormHttpMessageConverter() {
 if (!shouldIgnoreXml) {
 try {
 addPartConverter(new SourceHttpMessageConverter<>());
 }
 catch (Error err) {
 // Ignore when no TransformerFactory implementation is available
 }
 if (jaxb2Present && !jackson2XmlPresent) {
 addPartConverter(new Jaxb2RootElementHttpMessageConverter());
 }
 }
 if (kotlinSerializationJsonPresent) {
 addPartConverter(new KotlinSerializationJsonHttpMessageConverter());
 }
 if (jackson2Present) {
 addPartConverter(new MappingJackson2HttpMessageConverter());
 }
 else if (gsonPresent) {
 addPartConverter(new GsonHttpMessageConverter());
 }
 else if (jsonbPresent) {
 addPartConverter(new JsonbHttpMessageConverter());
 }
 if (jackson2XmlPresent && !shouldIgnoreXml) {
 addPartConverter(new MappingJackson2XmlHttpMessageConverter());
 }
 if (jackson2SmilePresent) {
 addPartConverter(new MappingJackson2SmileHttpMessageConverter());
 }
}
在依赖中引入jackson包后,容器会把 MappingJackson2HttpMessageConverter 自 动注册到messageConverters 链的末尾。
Spring会根据返回的数据类型, 从 messageConverters 链选择合适的 HttpMessageConverter :
  • 当返回的数据是非字符串时, 使用的 MappingJackson2HttpMessageConverter 写入返回对象。
  • 当返回的数据是字符串时,StringHttpMessageConverter 会先被遍历到,这时会认为 StringHttpMessageConverter 可以使用。
public abstract class AbstractMessageConverterMethodProcessor extends
AbstractMessageConverterMethodArgumentResolver
implements HandlerMethodReturnValueHandler {

//...代码省略
protected <T> void writeWithMessageConverters(@Nullable T value,
MethodParameter returnType,
ServletServerHttpRequest inputMessage, ServletServerHttpResponse
outputMessage)
throws IOException, HttpMediaTypeNotAcceptableException,
HttpMessageNotWritableException {

//...代码省略
if (selectedMediaType != null) {
selectedMediaType = selectedMediaType.removeQualityValue();
for (HttpMessageConverter<?> converter : this.messageConverters) {
GenericHttpMessageConverter genericConverter = (converter
instanceof GenericHttpMessageConverter ?
(GenericHttpMessageConverter<?>) converter : null);
if (genericConverter != null ?
((GenericHttpMessageConverter)
converter).canWrite(targetType, valueType, selectedMediaType) :
converter.canWrite(valueType, selectedMediaType)) {

//getAdvice().beforeBodyWrite 执⾏之后, body转换成了Result类型的结果
body = getAdvice().beforeBodyWrite(body, returnType,
selectedMediaType,
(Class<? extends HttpMessageConverter<?>>)
converter.getClass(),
inputMessage, outputMessage);
if (body != null) {
Object theBody = body;
LogFormatUtils.traceDebug(logger, traceOn ->"Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
addContentDispositionHeader(inputMessage, outputMessage);
   if (genericConverter != null) {
 genericConverter.write(body, targetType, 
selectedMediaType, outputMessage);
 }
 else {
 //此时cover为StringHttpMessageConverter
 ((HttpMessageConverter) converter).write(body, 
selectedMediaType, outputMessage);
 }
 }
 else {
 if (logger.isDebugEnabled()) {
 logger.debug("Nothing to write: null body");
 }
 }
 return;
 }
 }
 }
 //...代码省略
 
 }
 //...代码省略
}
((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage) 的处理中, 调用父类的write方法。由于 StringHttpMessageConverter 重写了addDefaultHeaders方法,所以会执行子类的方法。
然而子类 StringHttpMessageConverter 的addDefaultHeaders方法定义接收参数为String,此时 t 为 Result 类型,,所以出现类型不匹配"Result cannot be cast to java.lang.String"的异常。

三、优点

  1. 方便前端程序员更好的接收和解析后端数据接口返回的数据;
  2. 降低前端程序员和后端程序员的沟通成本, 按照某个格式实现就可以了, 因为所有接口都是这样返回的;
  3. 有利于项目统一数据的维护和修改;
  4. 有利于后端技术部门的统一规范的标准制定, 不会出现稀奇古怪的返回内容。