在前后端分离的项目中后端返回的格式一定要友好,不然会对前端的开发人员带来很多的工作量。那么SpringBoot如何做到统一的后端返回格式呢?今天我们一起来看看。

为什么要对SpringBoot返回统一的标准格式

在默认情况下,SpringBoot的返回格式常见的有三种:

返回String

@GetMapping("/hello")
public String hello() {
return "hello";
}

此时调用接口获取到的返回值是这样:

hello

返回自定义对象

@GetMapping("/student")
public Student getStudent() {
Student student = new Student();
student.setId(1);
student.setName("didiplus");
return student;
} //student的类
@Data
public class Student {
private Integer id;
private String name;
}

此时调用接口获取到的返回值是这样:

{"id":1,"name":"didiplus"}

接口异常

@GetMapping("/error")
public int error(){
int i = 9/0;
return i;
}

此时调用接口获取到的返回值是这样:

SpringBoot的版本是v2.6.7,

定义返回对象

package com.didiplus.common.web.response;

import lombok.Data;

import java.io.Serializable;

/**
* Author: didiplus
* Email: 972479352@qq.com
* CreateTime: 2022/4/24
* Desc: Ajax 返 回 JSON 结 果 封 装 数 据
*/ @Data
public class Result<T> implements Serializable { /**
* 是否返回成功
*/
private boolean success; /**
* 错误状态
*/
private int code; /***
* 错误信息
*/
private String msg; /**
* 返回数据
*/
private T data; /**
* 时间戳
*/
private long timestamp ; public Result (){
this.timestamp = System.currentTimeMillis();
}
/**
* 成功的操作
*/
public static <T> Result<T> success() {
return success(null);
} /**
* 成 功 操 作 , 携 带 数 据
*/
public static <T> Result<T> success(T data){
return success(ResultCode.RC100.getMessage(),data);
} /**
* 成 功 操 作, 携 带 消 息
*/
public static <T> Result<T> success(String message) {
return success(message, null);
} /**
* 成 功 操 作, 携 带 消 息 和 携 带 数 据
*/
public static <T> Result<T> success(String message, T data) {
return success(ResultCode.RC100.getCode(), message, data);
} /**
* 成 功 操 作, 携 带 自 定 义 状 态 码 和 消 息
*/
public static <T> Result<T> success(int code, String message) {
return success(code, message, null);
} public static <T> Result<T> success(int code,String message,T data) {
Result<T> result = new Result<T>();
result.setCode(code);
result.setMsg(message);
result.setSuccess(true);
result.setData(data);
return result;
} /**
* 失 败 操 作, 默 认 数 据
*/
public static <T> Result<T> failure() {
return failure(ResultCode.RC100.getMessage());
} /**
* 失 败 操 作, 携 带 自 定 义 消 息
*/
public static <T> Result<T> failure(String message) {
return failure(message, null);
} /**
* 失 败 操 作, 携 带 自 定 义 消 息 和 数 据
*/
public static <T> Result<T> failure(String message, T data) {
return failure(ResultCode.RC999.getCode(), message, data);
} /**
* 失 败 操 作, 携 带 自 定 义 状 态 码 和 自 定 义 消 息
*/
public static <T> Result<T> failure(int code, String message) {
return failure(ResultCode.RC999.getCode(), message, null);
} /**
* 失 败 操 作, 携 带 自 定 义 状 态 码 , 消 息 和 数 据
*/
public static <T> Result<T> failure(int code, String message, T data) {
Result<T> result = new Result<T>();
result.setCode(code);
result.setMsg(message);
result.setSuccess(false);
result.setData(data);
return result;
} /**
* Boolean 返 回 操 作, 携 带 默 认 返 回 值
*/
public static <T> Result<T> decide(boolean b) {
return decide(b, ResultCode.RC100.getMessage(), ResultCode.RC999.getMessage());
} /**
* Boolean 返 回 操 作, 携 带 自 定 义 消 息
*/
public static <T> Result<T> decide(boolean b, String success, String failure) {
if (b) {
return success(success);
} else {
return failure(failure);
}
}
}

定义状态码

package com.didiplus.common.web.response;

import lombok.Getter;

/**
* Author: didiplus
* Email: 972479352@qq.com
* CreateTime: 2022/4/24
* Desc: 统 一 返 回 状 态 码
*/
public enum ResultCode {
/**操作成功**/
RC100(100,"操作成功"),
/**操作失败**/
RC999(999,"操作失败"),
/**服务限流**/
RC200(200,"服务开启限流保护,请稍后再试!"),
/**服务降级**/
RC201(201,"服务开启降级保护,请稍后再试!"),
/**热点参数限流**/
RC202(202,"热点参数限流,请稍后再试!"),
/**系统规则不满足**/
RC203(203,"系统规则不满足要求,请稍后再试!"),
/**授权规则不通过**/
RC204(204,"授权规则不通过,请稍后再试!"),
/**access_denied**/
RC403(403,"无访问权限,请联系管理员授予权限"),
/**access_denied**/
RC401(401,"匿名用户访问无权限资源时的异常"),
/**服务异常**/
RC500(500,"系统异常,请稍后重试"), INVALID_TOKEN(2001,"访问令牌不合法"),
ACCESS_DENIED(2003,"没有权限访问该资源"),
CLIENT_AUTHENTICATION_FAILED(1001,"客户端认证失败"),
USERNAME_OR_PASSWORD_ERROR(1002,"用户名或密码错误"),
UNSUPPORTED_GRANT_TYPE(1003, "不支持的认证模式"); /**自定义状态码**/
@Getter
private final int code; /**
* 携 带 消 息
*/
@Getter
private final String message;
/**
* 构 造 方 法
*/
ResultCode(int code, String message) { this.code = code; this.message = message;
}
}

统一返回格式

    @GetMapping("/hello")
public Result<String> hello() {
return Result.success("操作成功","hello");
}

此时调用接口获取到的返回值是这样:

{"success":true,"code":100,"msg":"操作成功","data":"hello","timestamp":1650785058049}

这样确实已经实现了我们想要的结果,我在很多项目中看到的都是这种写法,在Controller层通过Result.success()对返回结果进行包装后返回给前端。这样显得不够专业而且不够优雅。 所以呢我们需要对代码进行优化,目标就是不要每个接口都手工制定Result返回值。

高级实现方式

要优化这段代码很简单,我们只需要借助SpringBoot提供的ResponseBodyAdvice即可。

ResponseBodyAdvice的源码:

public interface ResponseBodyAdvice<T> {
/**
* 是否支持advice功能
* true 支持,false 不支持
*/
boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2); /**
* 对返回的数据进行处理
*/
@Nullable
T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
}

只需要编写一个具体实现类即可

@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> { @Autowired
ObjectMapper objectMapper; @Override
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
return true;
} @SneakyThrows
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
if (body instanceof String){
return objectMapper.writeValueAsString(Result.success(ResultCode.RC100.getMessage(),body));
}
return Result.success(ResultCode.RC100.getMessage(),body);
}
}

需要注意两个地方:

@RestControllerAdvice注解 @RestControllerAdvice是@RestController注解的增强,可以实现三个方面的功能:

  1. 全局异常处理
  2. 全局数据绑定
  3. 全局数据预处理

String类型判断

        if (body instanceof  String){
return objectMapper.writeValueAsString(Result.success(ResultCode.RC100.getMessage(),body));
}

这段代码一定要加,如果Controller直接返回String的话,SpringBoot是直接返回,故我们需要手动转换成json。 经过上面的处理我们就再也不需要通过ResultData.success()来进行转换了,直接返回原始数据格式,SpringBoot自动帮我们实现包装类的封装。

    @GetMapping("/hello")
public String hello() {
return "hello,didiplus";
} @GetMapping("/student")
public Student getStudent() {
Student student = new Student();
student.setId(1);
student.setName("didiplus");
return student;
}

此时我们调用接口返回的数据结果为:

{
"success": true,
"code": 100,
"msg": "操作成功",
"data": "hello,didiplus",
"timestamp": 1650786993454
}

SpringBoot 如何统一后端返回格式的更多相关文章

  1. SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!

    大家好,我是飘渺. 今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常. 首先我们来看看为什么要返回统一的标准格式? 为什么要对Sp ...

  2. 定义统一的返回格式(controller)

    一:单独创建一个类来表示返回结果 package com.jk51.commons.dto; /** * Created by Administrator on 2017/6/13. */ publi ...

  3. spring boot:使接口返回统一的RESTful格式数据(spring boot 2.3.1)

    一,为什么要使用REST? 1,什么是REST? REST是软件架构的规范体系,它把资源的状态用URL进行资源定位, 以HTTP动作(GET/POST/DELETE/PUT)描述操作 2,REST的优 ...

  4. 解决spring boot中rest接口404,500等错误返回统一的json格式

    在开发rest接口时,我们往往会定义统一的返回格式,列如: { "status": true, "code": 200, "message" ...

  5. SpringBoot系列(十)优雅的处理统一异常处理与统一结果返回

    SpringBoot系列(十)统一异常处理与统一结果返回 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列 ...

  6. 自定义统一api返回json格式(app后台框架搭建三)

    在统一json自定义格式的方式有多种:1,直接重写@reposeBody的实现,2,自定义一个注解,自己去解析对象成为json字符串进行返回 第一种方式,我就不推荐,想弄得的话,可以自己去研究一下源码 ...

  7. ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

    现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间.我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作 ...

  8. springboot实现定时任务,异步操作,统一结果返回,全局异常处理,拦截器及事务处理

    本文是作者原创,版权归作者所有.若要转载,请注明出处. 本文都是springboot的常用和实用功能,话不多说开始吧 定时任务 1.启动类开启注解 @EnableScheduling //开启基于注解 ...

  9. Spring Boot API 统一返回格式封装

    今天给大家带来的是Spring Boot API 统一返回格式封装,我们在做项目的时候API 接口返回是需要统一格式的,只有这样前端的同学才可对接口返回的数据做统一处理,也可以使前后端分离 模式的开发 ...

随机推荐

  1. 写出Hibernate中核心接口/类的名称,并描述他们各自的责任?

    Hibernate的核心接口一共有5个,分别为:Session.SessionFactory.Transaction.Query和 Configuration.这5个核心接口在任何开发中都会用到.通过 ...

  2. pg数据库org.postgresql.util.PSQLException: ERROR: "xxx" is not a sequence

    问题场景 对pg数据表执行插入语句的时候,报错如下: { "timestamp": 1587012576734, "status": 500, "er ...

  3. String、StringBuiler、StringBuffer的区别

    一.三者的区别概述 1.可变与不可变:String底层使用final修饰的字符数组来存储字符串,它属于不可变类,对String对象的任何改变操作都不会改变原对象,而是生成一个新对象.StringBui ...

  4. 修改openstack Centos镜像的默认用户的密码

    Ubuntu官方提供的OpenStack镜像是用Key来登录的,太麻烦,可以改成用密码来登录. 修改image的工具叫:guestfish. yum install libguestfs-tools ...

  5. vue2与vue3的区别

    template <template> <div class="wrap"> <div>{{ num }}</div> <Bu ...

  6. 汽车中的低速can和高速can的区别

    转自:https://zhuanlan.zhihu.com/p/33212308

  7. 基于MPC算法的车辆多目标自适应巡航控制系统研究_荆亚杰

  8. 《CSS世界》笔记二:盒模型四大家族

    上一篇:<CSS世界>笔记一:流/元素/尺寸下一篇:<CSS世界>笔记三:内联元素与对齐 写在前面 在读<CSS世界>第四章之前,粗浅的认为盒模型无非是margin ...

  9. dva+react+antd+webpack 项目开发配置

    如何搭建一个dva项目如何搭建一个dva项目 后期项目会在github上进行书写,同时也会在segmentfault上进行同步-3Q拜读-

  10. php文件下载服务器代码

    事情的起因 额,平板想下载电脑上的pdf文件,我开启了web服务,局域网下的ipad访问该文件web路径会直接打开该pdf,而不是下载.于是本小白就折腾了一下. 源代码 <?php forceD ...