2023.12.5 关于 Spring Boot 统一数据格式返回

目录

引言

统一数据格式

实例理解

[特殊 String 类型处理](#特殊 String 类型处理)

实例理解

分析返回的流程

补充知识

分析报错原因

解决方案一

解决方案二

最终测试


引言

  • 统一数据格式能 方便前端程序员更好的接收和解析后端返回的数据
  • 统一数据格式能 降低约定前后端交互接口的成本,因为所有接口都是这样返回的
  • 统一数据格式 有利于项目统一数据的维护和修改

统一数据格式

  • 两步实现统一数据格式返回

实例理解

  • 创建一个 ResponseAdvice 类,并添加 @ControllerAdvice 注解
java 复制代码
package com.example.demo.component;

import org.springframework.web.bind.annotation.ControllerAdvice;

@ControllerAdvice
public class ResponseAdvice {
    
}
  • 实现 ResponseBodyAdvice 接口,重写 supports 和 beforeBodyWrite 方法
java 复制代码
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 方法用来测试
java 复制代码
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!",并希望其帮我们修改为标准的数据返回格式
java 复制代码
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 类型

分析返回的流程

  1. test3 方法准备返回 String 类型,返回前需要进行类型的判断,如果为 HashMap 类型则 直接返回
  2. 经判定此处不为 HashMap 类型,则统一 返回 数据格式 ------> 将 String 类型转换为 HashMap 类型
  3. 最后再将 HashMap 转换为 application/json 字符串给前端

补充知识

  • 在执行第三步时,Spring 框架会根据返回的类型进行判断,选择不同的转换器进行类型转换

分析报错原因

  • 正是因为 test3 方法的返回类型为 String 类型
  • 所以此时 HashMap 类型的数据 将会使用 StringHttpMessageConverter 转换器 来将其转化为 json 格式的字符串
  • 然而 StringHttpMessageConverter 转换器是专门用于处理 String 类型数据的
  • 这样就导致了 异常的发生,即在使用 StringHttpMessageConverter 转换器的前提下试图将 HashMap 对象强制转化成 String 类型(json格式的字符串为 String 类型)

解决方案一

  • 直接将 StringHttpMessageConverter 移除
java 复制代码
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
java 复制代码
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 方法进行访问调用
相关推荐
Joy T5 小时前
Agent 确定性上下文设计:从 Prompt 参数注入到能力分层
java·开发语言·微服务·架构·prompt·agent context
m0_587383008 小时前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析
·云扬·8 小时前
Prompt 工程与安全:从 System 设计到注入防御
java·人工智能·安全·ai·prompt
代码不停9 小时前
自动化测试(java)
java·开发语言·selenium·自动化
行百里er9 小时前
Redis 完结篇——Redis 热点问题在 Spring Boot 项目中实战
redis·后端
高频因子挖掘机10 小时前
只取最近 60 个交易日,却每次拉全历史:量化脚本变慢的原因与改法
后端·github·api
鬼手点金11 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
孟健11 小时前
从5000亿Token到83%字节匹配:AI逆向工程与重构的工程闭环实践
后端·架构·ai编程
paopaokaka_luck11 小时前
滑雪场管理系统(AI智能助手流式对话,ECharts 运营数据分析、教练、雪具与场地一体化预约,钱包支付退款、订单核销与服务评价)
java·人工智能·spring boot·网络协议·数据分析·echarts
SL_staff12 小时前
JVS混合架构实践:能力引擎+动态模型如何支撑企业级可持续交付
spring boot·低代码·全栈