接口统一返回标准类型

Java接口统一返回标准类型

一、前言、背景

在如今前后端分离的时代,后端已经由传统的返回view视图转变为返回json数据,此json数据可能包括返回状态、数据、信息等…因为程序猿的习惯不同所以返回json数据的格式也各有千秋;此时迫切需要一个需求----将后台返回的数据再封装起来统一返回一个标准的类型供前端程序猿调用。

SpringBoot框架中,它已经给我们封装了一个标准的类型ResponseEntity,但是框架考虑的很多,很是冗余,因此我们需要自己动手编写适合自己使用的标准 我把他称之为R类型。循序渐进

二、返回的统一格式

在开发中 我常用 code 表示状态码、message表示返回的提示信息、data表示返回的具体数据

{
 "code": 200, //响应码
 "message": success, //返回消息
 "data": object //返回数据
}

三、创建统一返回类

1. 创建标准返回类

// 统一结果返回类
@Data
@AllArgsConstructor
public class R {
    //标识返回的状态码
    private Integer code;
    //标识返回的信息
    private String message;
    //标识返回的数据
    private Object data;

    //私有化,防止new
    private R() {  }
  
    //成功
    public static R ok(Object data, String message) {
        return new R(200, message, data);  //code 也可以使用字典管理 下面会谈到
    }

    //成功返回 重载 message没有特别要求
    public static R ok(Object data) {
        return R.ok(data, "success"); //message 也可以使用字典管理 下面会谈到
    }

    // 失败
    public static R error( Integer code, String message) {
        return new R(code, message, "");
    }
}

牛刀小试 返回结果

{
    "code": 200,
    "message": "成功",
    "data": {
        "id": 1,
        "account": "qd666",
        "passWord": "123456"
    }
}

2. 统一字典管理

在上述成功的案例中,code 的值为200,而在失败中就不止这一种情况 所以需要改进

新建RConstants接口 统一维护

//返回常量
public interface RConstants {
    Integer FAIL_CODE=201;
    String FAIL_MESSAGE="查询失败";
    //......
}

调用

 return R.error(RConstants.FAIL_CODE, RConstants.FAIL_MESSAGE); //201,查询失败

结果

{
    "code": 201,
    "message": "查询失败",
    "data": ""
}

3. 枚举类型匹配

在上述接口中,我们发现,如果一个code 如果要对应message的话,需要创建大量的自定义常量,非常麻烦,这时我们可以改变其为枚举类型使我们的代码更加简介 但是如果只有一个属性的话,使用枚举就多此一举了

创建RHttpStatusEnum 枚举

public enum RHttpStatusEnum {

    //举个栗子 查询失败与登录失败
    QUERY_USER_FAIL(201, "查询失败"),
    LOGIN_USER_FAIL(202, "登录失败");
  
	  final int code;
    final String message;
    RHttpStatusEnum(int code, String message) {
        this.code = code;
        this.message = message;
    }
 //生成get方法  已经赋值了所以不需要set方法
}  

调用

return R.error(RHttpStatusEnum.QUERY_USER_FAIL.getCode(),
                RHttpStatusEnum.QUERY_USER_FAIL.getMessage());

当我们使用了枚举之后,发现在调用R类时比较复杂 我们可以改变R类的error方法


4. 简化R类方法

修改error方法

    // 失败
    public static R error(RHttpStatusEnum httpStatusEnum) {
        return new R(httpStatusEnum.getCode(),httpStatusEnum.getMessage(),"");
    }

调用 参考SpringBootHttpStatus

return R.error(RHttpStatusEnum.QUERY_USER_FAIL);

ok !写道这里就发现我们定义的这个标准格式已经非常完美了 满足日常开发的使用了 但同时也会有丝丝缺点,因为这个R类把后端返回的数据限制住了,开发人员必须在实际代码中都要返回这个R类,降低了扩展性 。

解决方案: 在开发之中,程序猿想要随心所欲的返回任何的数据,我们只需使用R类对其封装一层即可! 利用Spring的后置机制可以实现该要求。 个人觉得不在包装也是很好的,依据自己的情况而定!

四、封装R类

可以使用spring提供的结果拦截增强处理机制来解决这个问题,如下:

采用springboot提供的 ResponseBodyAdvicel处理即可。

ResponseBodyAdvice的作用是:拦截Controller方法的返回值,统一处理返回值到响应体中,一般来做response的统一格式,加密,签名等。

  • 实现ResponseBodyAdvice接口,并扫描包名
@ControllerAdvice(basePackages = "com.qd.springboot") //扫描包
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;
    }

    //o = controller方法的返回值
    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //如果返回的是string会默认调用string的处理器直接返回
        if (o instanceof String) {
           return JSON.toJSONString(R.ok(o));
        }else if(o instanceof  R){
            return o;
        }
       return  R.ok(o);
    }
}
  • 导入需要的依赖
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.54</version>
        </dependency>
  • R类 如果没有太多的对应关系,直接用接口实现即可
@Data
@AllArgsConstructor
public class R {
    //标识返回的状态码
    private Integer code;
    //标识返回的信息
    private String message;
    //标识返回的数据
    private Object data;

    //私有化,防止new
    private R() {
    }
  
   //成功
    public static R ok(Object data, String message) {    
                return new R(200, message, data);  //code 也可以使用字典管理 下面会谈到 
       }    

    //成功返回 重载 message没有特别要求   
    public static R ok(Object data) {     
       return R.ok(data, "success"); //message 也可以使用字典管理 下面会谈到 
          }

    // 失败
    public static R error(Integer code, String message) {
        return new R(code, message, "");
    }
}
  • 字典管理
public interface RConstants {
    Integer FAIL_CODE=201;
    String FAIL_MESSAGE="查询失败";
    //......
}

这样设置后,需要捕获程序运行中的异常,对于不正确的请求,我们主动抛出异常,然后统一去捕获,最后调用R类error方法统一返回错误信息

  • GlobalExceptionHandler 全局异常
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public Object exceptionHandler1(RuntimeException e) {
        log.error("异常类型:{}--异常信息:{}", e.getClass(), e.getMessage());
        //code状态码可在字典中定义
        return R.error(201, e.getMessage());
    }
  
  
  //数据校验
      @ExceptionHandler(value = ConstraintViolationException.class)
    public Object ViolationExceptions(ConstraintViolationException e) {
        log.error("异常类型:{}--异常信息:{}", e.getClass(), e.getMessage());
        String ex = e.getMessage();
        String message = null;
        //判断是否多参数错误 是:一个个返回;否:返回
        if (ex.contains(",")) {
            message = ex.substring(ex.indexOf(":") + 1, ex.indexOf(","));
        } else {
            message = ex.split(":")[1].toString();
        }
        return R.error(201, message);
    }
	
  //其他的异常....
  

}
  • controller或者service 判断标志 主动抛出异常
    @GetMapping("user/id/{id}")
    public User queryUserByID(@PathVariable("id") Integer id) {
        if ("2".equals(id)) {
            throw new RuntimeException(RConstants.FAIL_MESSAGE);
        }
        return userService.queryUserById(id);
    }

到此,就学习的差不多了


The End~~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值