SpringBoot统一接口返回的标准格式R.java
1、分析
前后端分离的开发方式,通过swagger来进行接口测试。
每个开发者,对自己的代码都有一套自己的逻辑和哲学,返回值就千变万化。接口的调用者,对于返回值的不理解。
解决方法:统一返回处理
2、格式
# 成功的状态
{
code:200,
data:{id:"1",name:"yykkk"},
message:"success"
}
# 失败
{
code:401,
data:"",
message:"用户名和账号有误"
}
{
code:500,
data:"",
message:"服务器出错!!"
}
3、基础封装认识
3.1 封装
package com.example.common;
import lombok.Data;
@Data
public class R {
// 返回编号
private Integer code;
// 返回数据
private Object data;
// 返回信息
private String message;
}
3.2 使用
package com.example.controller;
import com.example.common.R;
import com.example.service.RegService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class RegController {
@Autowired
private RegService regService;
// 注册
@GetMapping("/reg")
public R reguser(){
// 1、注册用户
log.info("新用户注册");
// userService.save(user);
// 2、发送短信
log.info("新用户注册");
// messageService.sendMsg();
regService.sendMsg();
// 3、添加积分
log.info("新用户注册");
// scoreService.addScore(user);
regService.addScore();
R r = new R();
r.setCode(200);
r.setData("OK");
r.setMessage("注册成功");
return r;
}
}
3.3 启动
访问:http://localhost:8080/reg,结果如下:
{"code":200,"data":"ok","message":"注册成功!"}
3.4 问题
频繁的创建R类;第一会增加内存开销,第二:代码臃肿和冗余。
4、静态方法封装
4.1优化
用静态方法方法去优化和封装
4.2 实现
package com.example.common;
import lombok.Data;
@Data
public class R {
// 返回编号
private Integer code;
// 返回数据
private Object data;
// 返回信息
private String message;
public static R success(Object data, String message) {
R r = new R();
r.setCode(200);
r.setData(data);
r.setMessage(message);
return r;
}
public static R success(Object data) {
return success(data, "");
}
}
4.3 启动
访问:http://localhost:8080/reg,结果如下:
{"code":200,"data":"ok","message":"注册成功!"}
4.3 解决问题
代码臃肿和冗余。
5、失败封装
5.1 封装
package com.example.common;
import lombok.Data;
@Data
public class R {
// 返回编号
private Integer code;
// 返回数据
private Object data;
// 返回信息
private String message;
/**
* @Description 成功返回
* @param code 失败状态
* @param message 失败原因
* @return
*/
public static R fail(Integer code, String message) {
R r = new R();
r.setCode(code);
r.setData(null);
r.setMessage(message);
return r;
}
}
5.2 测试
package com.example.controller;
import com.example.common.R;
import com.example.service.RegService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class RegController {
@Autowired
private RegService regService;
// 注册
@GetMapping("/reg")
public R reguser(Integer flag){
if (flag.equals(1)) {
return R.fail(401, "用户名或密码错误");
}
if(flag.equals(2)){
return R.fail(402, "两次输入密码不一致");
}
// 1、注册用户
log.info("新用户注册");
// userService.save(user);
// 2、发送短信
log.info("新用户注册");
// messageService.sendMsg();
regService.sendMsg();
// 3、添加积分
log.info("新用户注册");
// scoreService.addScore(user);
regService.addScore();
// R r = new R();
// r.setCode(200);
// r.setData("OK");
// r.setMessage("注册成功");
return R.success("OK", "注册成功");
}
}
5.3 测试结果
http://localhost:8080/reg2?flag=0 正常返回
{"code":200,"data":"ok","message":""}
http://localhost:8080/reg2?flag=1 失败返回
{"code":401,"data":null,"message":"用户名或密码错误!!!"}
http://localhost:8080/reg2?flag=2 正常返回
{"code":402,"data":null,"message":"两次输入密码不一致!!!"}
6、构造函数私有化
为什么要构造函数私有化呢?
使调用的过程变得单一,只允许用类去调用方法,不允许用new去调用。
private R(){}
7、统一返回状态的维护和message的维护问题
7.1 接口或者常量类
package com.example.common;
public class RConstants {
// 用户名和密码错误信息和状态
public static final Integer USER_REG_USER_PASSWORD_CODE = 401;
public static final String USER_REG_USER_PASSWORD_ERROR = "用户名或密码错误!";
// 密码和确认密码错误信息和状态
public static final Integer USER_REG_USER_PASSWORD_CONFIRM_CODE = 402;
public static final String USER_REG_USER_PASSWORD_CONFIRM_ERROR = "两次输入密码不一致!";
}
if (flag.equals(1)) {
return R.fail(RConstants.USER_REG_USER_PASSWORD_CODE, RConstants.USER_REG_USER_PASSWORD_ERROR);
}
if(flag.equals(2)){
return R.fail(RConstants.USER_REG_USER_PASSWORD_CONFIRM_CODE, RConstants.USER_REG_USER_PASSWORD_CONFIRM_ERROR);
}
问题:当错误状态和信息越来越多的时候,可维护会出现很多问题。
7.2 枚举
package com.example.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum ResponseEnum {
USER_REG_USER_PASSWORD_CODE(401, "用户名或密码错误"),
USER_REG_USER_PASSWORD_CONFIRM(402, "两次输入密码不一致");
private Integer code;
private String message;
}
if (flag.equals(1)) {
return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CODE.getCode(),
ResponseEnum.USER_REG_USER_PASSWORD_CODE.getMessage());
}
if (flag.equals(2)) {
return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CONFIRM.getCode(),
ResponseEnum.USER_REG_USER_PASSWORD_CONFIRM.getMessage());
}
7.3 最终方案
package com.example.common;
import lombok.Data;
@Data
public class R {
// 返回编号
private Integer code;
// 返回数据
private Object data;
// 返回信息
private String message;
// 构造函数私有化
private R(){}
/**
* @Description 成功返回
* @param data
* @param message
* @return
*/
public static R success(Object data, String message) {
R r = new R();
r.setCode(ResponseEnum.SUCCESS.getCode());
r.setData(data);
r.setMessage(message == null ? ResponseEnum.SUCCESS.getMessage() : message);
return r;
}
// 成功返回
public static R success(Object data) {
return success(data, "");
}
/**
*
* @param responseEnum
* @return
*/
public static R fail(ResponseEnum responseEnum) {
R r = new R();
r.setCode(responseEnum.getCode());
r.setData(null);
r.setMessage(responseEnum.getMessage());
return r;
}
}
package com.example.common;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Auther: 长颈鹿
* @Date: 2021/07/21/13:03
* @Description:
*/
@Getter
@AllArgsConstructor
public enum ResponseEnum {
SUCCESS(200,"成功!"),
USER_REG_USER_PASSWORD_CODE(401, "用户名或密码错误"),
USER_REG_USER_PASSWORD_CONFIRM(402, "两次输入密码不一致"),
ORDER_FAIL(601,"订单失败"),
ORDER_MESSAGE_FAIL(602,"订单发送消息失败") ;
private Integer code;
private String message;
}
if (flag.equals(1)) {
return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CODE);
}
if (flag.equals(2)) {
return R.fail(ResponseEnum.USER_REG_USER_PASSWORD_CONFIRM);
}
8、统一返回的参考来源
参考:
1、mybatis-plus–R类
2、springmvc:ResponseEntity和HttpStatus一个枚举类,如下:
@GetMapping("/reg2")
public ResponseEntity reguser2(Integer flag) {
// 1: 注册用户 10ms
log.info("新用户注册");
//userService.save(user);
// 2: 发送短信 5s
log.info("发送短信");
regService.sendMsg();
// 3: 添加积分 5s
log.info("添加积分");
regService.addScore();
// 失败
//return ResponseEntity.status(HttpStatus.BAD_GATEWAY);
// 成功
return ResponseEntity.ok("success");
}
不使用springMvc提供枚举类的原因:
- Springmvc满足不了业务开发需求
- 状态的控制和返回和业务可能没有太大的匹配关系。不明确
9、统一返回的拦截处理
9.1 问题
每写一个接口或者方法,都需要进行R返回,开发太过于强制和依赖R类。
9.2 ResponseBodyAdvice
使用spring提供的结果拦截增强处理机制来解决这个问题
采用springboot提供的ResponseBodyAdvice
l处理即可。
9.2.1 定义
利用spring中的AOP机制来完成的一种,对springmvc请求过程中对请求方法的结果进行增强的通知。会对你的结果进行加密,加工等处理,把加工的数据返回给浏览器。
9.2.2 作用
拦截Controller方法的返回值,统一处理返回值到响应体中,一般来做response的统一格式,加密,签名等。
9.2.3 注意事项
在使用的时候在controller的包上增加basePackages=”com.example”,如果不加的话,可能会对整个系统产生冲突影响,比如:如果使用了swagger时会出现空白异常。
package com.example.config;
import com.example.common.R;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
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(basePackages = "com.example")
public class ResultResponseHandler implements ResponseBodyAdvice<Object> {
/**
* 是否支持advice功能,true是支持,false是不支持
* @param methodParameter
* @param aClass
* @return
*/
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
/**
*
* @param o 代表springMvc的请求方法的结果
* @param methodParameter
* @param mediaType
* @param aClass
* @param serverHttpRequest
* @param serverHttpResponse
* @return
*/
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
// 对返回的结果进行统一返回和处理
return R.success(o);
}
}
9.3 结果
{
"code": 200,
"data": {
"id": 1,
"nickname": "中山靖王之后",
"password": "123456",
"avatar": null,
"adddress": "河北省涿州市"
},
"message": ""
}