SpringBoot统一接口返回的标准格式R.java

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提供的ResponseBodyAdvicel处理即可。

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": ""
}

版权声明:本文为mutf7原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。