SpringBoot 如何统一后端返回格式
在前后端分离的项目中后端返回的格式一定要友好,不然会对前端的开发人员带来很多的工作量。那么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注解的增强,可以实现三个方面的功能:
- 全局异常处理
- 全局数据绑定
- 全局数据预处理
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 如何统一后端返回格式的更多相关文章
- SpringBoot 如何统一后端返回格式?老鸟们都是这样玩的!
大家好,我是飘渺. 今天我们来聊一聊在基于SpringBoot前后端分离开发模式下,如何友好的返回统一的标准格式以及如何优雅的处理全局异常. 首先我们来看看为什么要返回统一的标准格式? 为什么要对Sp ...
- 定义统一的返回格式(controller)
一:单独创建一个类来表示返回结果 package com.jk51.commons.dto; /** * Created by Administrator on 2017/6/13. */ publi ...
- spring boot:使接口返回统一的RESTful格式数据(spring boot 2.3.1)
一,为什么要使用REST? 1,什么是REST? REST是软件架构的规范体系,它把资源的状态用URL进行资源定位, 以HTTP动作(GET/POST/DELETE/PUT)描述操作 2,REST的优 ...
- 解决spring boot中rest接口404,500等错误返回统一的json格式
在开发rest接口时,我们往往会定义统一的返回格式,列如: { "status": true, "code": 200, "message" ...
- SpringBoot系列(十)优雅的处理统一异常处理与统一结果返回
SpringBoot系列(十)统一异常处理与统一结果返回 往期推荐 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 springBoot系列 ...
- 自定义统一api返回json格式(app后台框架搭建三)
在统一json自定义格式的方式有多种:1,直接重写@reposeBody的实现,2,自定义一个注解,自己去解析对象成为json字符串进行返回 第一种方式,我就不推荐,想弄得的话,可以自己去研究一下源码 ...
- ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)
现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间.我们需要把所有接口及异常错误信息都返回一定的Json格式,有利于前端处理,从而提高了工作 ...
- springboot实现定时任务,异步操作,统一结果返回,全局异常处理,拦截器及事务处理
本文是作者原创,版权归作者所有.若要转载,请注明出处. 本文都是springboot的常用和实用功能,话不多说开始吧 定时任务 1.启动类开启注解 @EnableScheduling //开启基于注解 ...
- Spring Boot API 统一返回格式封装
今天给大家带来的是Spring Boot API 统一返回格式封装,我们在做项目的时候API 接口返回是需要统一格式的,只有这样前端的同学才可对接口返回的数据做统一处理,也可以使前后端分离 模式的开发 ...
随机推荐
- 什么是Hystrix?
防雪崩利器,具备服务降级,服务熔断,依赖隔离,监控(Hystrix Dashboard)服务降级:双十一 提示 哎哟喂,被挤爆了. app秒杀 网络开小差了,请稍后再试.优先核心服务,非核心服务不可用 ...
- Docker 的目的是什么?
Docker 提供了一个可用于托管任何应用程序的容器环境.在此,软件应用程序和 支持它的依赖项紧密打包在一起. 因此,这个打包的产品被称为 Container,因为它是由 Docker 完成的,所以它 ...
- SQLyog创建用户并授权的过程
点击你要授权的数据库然后点击用户管理器 然后输入用户名和密码主机选localhost 然后点击创建,然后选择你创建的数据库全选 最后保存就可以了
- Java 中的编译期常量是什么?使用它又什么风险?
公共静态不可变(public static final )变量也就是我们所说的编译期常量,这里 的 public 可选的.实际上这些变量在编译时会被替换掉,因为编译器知道这些 变量的值,并且知道这些变 ...
- 有哪些类型的通知Advice?
Before - 这些类型的 Advice 在 joinpoint 方法之前执行,并使用 @Before 注解标记进行配置. After Returning - 这些类型的 Advice 在连接点方法 ...
- 『忘了再学』Shell基础 — 6、Bash基本功能(输入输出重定向)
目录 1.Bash的标准输入输出 2.输出重定向 (1)标准输出重定向 (2)标准错误输出重定向 (3)正确输出和错误输出同时保存 3.输入重定向 1.Bash的标准输入输出 我们前边一直在说,在Li ...
- JDBC和桥接模式
本文参考 网上对于JDBC与桥接模式的理解各有不同,在这片文章里提出的是我个人对于二者的理解,本文参考的其它博文如下: https://blog.csdn.net/paincupid/article/ ...
- 一个未知宽高的元素在div中垂直水平居中
<body> <div id="#div1"> <img src="img1.png"></img> </ ...
- 基于react的audio组件
样式请自己定义哦~需要其他功能请自行添加! // 组件调用 <Audio src={src地址} id={srcID}/> audio属性 src 歌曲的路径 preload 是否在页面加 ...
- 用css动态实现圆环百分比分配——初探css3动画
最近的小程序项目有个设计图要求做一个圆环,两种颜色分配,分别代表可用金额和冻结金额.要是就直接这么显示,感觉好像挺没水平??于是我决定做个动态! 在mdn把新特性gradients(渐变).trans ...