Swagger2 | 04. 统一返回对象

1. 过去的方式(存在的问题)

  • 之前统一封装返回值的时候,使用如下的格式:

在这里插入图片描述

  • 如果数据直接用Object表示,那么在swagger中,就会变成这样:

在这里插入图片描述
接口文档无法解析存储在其中的数据。

2. 解决方案

为了正确返回数据,需要用泛型来声明这个data,而在真正返回时,再声明具体的类型。

2.1. 声明泛型

在这里插入图片描述

如上图所示,在类代码中使用【泛型T】来代替原来的【Object】

随后在使用时,具体声明它的类型

在这里插入图片描述
这样当swagger在解析这个Result类的时候,就能知道“T”的类型为Email类

那么在解析data这个属性时,便会认为data是Email类,就会按照Email对象的方式去解析

在这里插入图片描述

2.2. 注意细节

在这里插入图片描述
可以看到,在上面的返回值中使用了Result

而在下面的return代码中,返回的只用了Result<>

这里可以类比 Map<String,Object> map = new HashMap<>();

3. 统一的返回对象

3.1. ResultInformation.java:定义状态码和提示信息

  • 在common包下创建ResultInformation.java

在这里插入图片描述

3.2. Result.java:统一的返回对象

  • 在util包下创建Result.java,记得:在类代码和对应的data属性代码中加上泛型

在这里插入图片描述

  • 针对controller的各种业务场景,声明五个不同的构造方法。此处讲解最复杂的构造方法:

在这里插入图片描述

  • 这里的dataKey和dataVlue是从Controller中传入的,也就是手动传入,随后将这两个数据封装成map,赋给Result的data属性
  • 这里的ResultInformation.SUCCESS是获取成功时候的状态码。换言之,如果传入的code不与成功的状态码相等,则说明是失败
  • 此段代码的意思,其实是根据code,返回对应的中文信息。code和中文信息的对应关系,被保存在ResultInformation.messageMap中,因此用get(code)就能返回中文信息。

3.3. 新增:自定义异常和对异常的处理

有时会发生期望范围内的异常(如库存不足、输入的账号密码不符合规范等)

对于这些情况,不仅希望程序能将状态码、中文信息返回,还希望抛出异常,并使代码回滚,此时便引出该部分:

  • 先定义自定义异常类CustomException.java

    image-20210816013206060

  • 在统一返回对象Result.java中,创建构造方法对异常进行处理

    image-20210816014016107

4. 源码

2021.04.06修订:

  1. 为ResultInformation新增successMap属性,该属性使得可以有多个状态码使Result判定返回状态为“成功”
  2. 为Result新增toString方法,用junit进行单元测试时返回值更加直观

2021.08.16修订:

  1. 业务中较少用到Result中的success属性,通常只要code和message便足矣,因此删去boolean类型属性success,同时删去ResultImformation中的successMap属性
  2. 新增对异常的处理和手动的事务回滚,相关说明已在3.3中呈现
  3. Result更名为ResultUtil,ResultInformation更名为ResultConstant

4.1. ResultConstant.java(在constant包下)

package com.eshang.my.basket.background.constant;

import java.util.HashMap;

public class ResultConstant {
	public static HashMap<Integer, String> messageMap = new HashMap<>();
	//成功或失败,状态码都是1开头
	public static int SUCCESS = 10000;
	public static int FAILED = 10001;
	//交互
	public static int LIKE_SUCCESS = 10011;
	public static int COLLECT_SUCCESS = 10012;
	public static int LIKE_CANCEL = 10013;
	public static int COLLECT_CANCEL = 10014;
	public static int IS_LIKE = 10015;
	public static int IS_NOT_LIKE = 10016;
	public static int IS_COLLECT = 10017;
	public static int IS_NOT_COLLECT = 10018;
	//用户
	public static int USER_NAME_IS_NULL = 10021;
	public static int USER_PASSWORD_IS_NULL = 10022;
	public static int USER_NICKNAME_IS_NULL = 10023;
	public static int CONFIRM_PASSWORD_IS_NULL = 10024;
	public static int CONFIRM_PASSWORD_IS_WRONG = 10025;
	public static int USER_PHONE_IS_NULL = 10026;
	//异常:20000
	public static int ERROR = 20000;

	static {
		//成功或失败
		messageMap.put(10000, "成功");
		messageMap.put(10001, "失败");
		//交互
		messageMap.put(10011, "点赞成功");
		messageMap.put(10012, "收藏成功");
		messageMap.put(10013, "成功取消点赞");
		messageMap.put(10014, "成功取消收藏");
		messageMap.put(10015, "已点赞");
		messageMap.put(10016, "未点赞");
		messageMap.put(10017, "已收藏");
		messageMap.put(10018, "未收藏");
		//用户
		messageMap.put(10021, "未输入账号");
		messageMap.put(10022, "未输入密码");
		messageMap.put(10023, "未输入昵称");
		messageMap.put(10024, "未确认密码");
		messageMap.put(10025, "两次密码不同");
		messageMap.put(10026, "未输入联系方式");
		//异常
		messageMap.put(20000, "系统异常");
	}


}

4.2. ResultUtil.java(在util包下)

package com.eshang.my.basket.background.util;

import com.eshang.my.basket.background.constant.ResultConstant;
import com.eshang.my.basket.background.exception.CustomException;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import java.util.HashMap;
import java.util.Map;


/**
 * @author xyx-Eshang
 */
public class ResultUtil<T> {
	@ApiModelProperty(value = "状态码", example = "200")
	private Integer code;

	@ApiModelProperty(value = "提示信息", example = "成功")
	private String message;

	@ApiModelProperty(value = "返回数据")
	private Map<String, T> data;

	/**
	 * 01 空参构造:成功
	 */
	public ResultUtil() {
		this.code = ResultConstant.SUCCESS;
		this.message = ResultConstant.messageMap.get(code);
	}

	/**
	 * 02. 只有状态码的构造方法
	 *
	 * @param code
	 */
	public ResultUtil(Integer code) {
		this.code = code;
		this.message = ResultConstant.messageMap.get(code);
	}

	/**
	 * 03. 有key、有value的构造方法
	 *
	 * @param dataKey
	 * @param dataValue
	 */
	public ResultUtil(String dataKey, T dataValue) {
		this.code = ResultConstant.SUCCESS;
		this.message = ResultConstant.messageMap.get(code);
		Map<String, T> dataMap = new HashMap<>();
		dataMap.put(dataKey, dataValue);
		this.data = dataMap;
	}

	/**
	 * 04. 有code,有key,有value的构造方法
	 *
	 * @param code
	 * @param dataKey
	 * @param dataValue
	 */
	public ResultUtil(Integer code, String dataKey, T dataValue) {
		this.code = code;
		this.message = ResultConstant.messageMap.get(code);
		Map<String, T> dataMap = new HashMap<>();
		dataMap.put(dataKey, dataValue);
		this.data = dataMap;
	}

	/**
	 * 05. 异常时:回滚
	 *
	 * @param exception
	 */
	public ResultUtil(Exception exception) {
		try {
			//手动回滚
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		} catch (NoTransactionException e) {
			//如果方法没有开启事务,那么这里会抛出异常。不处理即可
		}
		if (exception.getClass().getSimpleName().contentEquals(CustomException.class.getSimpleName())) {
			CustomException e = (CustomException) exception;
			this.code = e.getCode();
			this.message = e.getMessage();
			System.out.println("===发生自定义异常:" + this.message + "===");
		} else {
			exception.printStackTrace();
			this.code = 40000;
			this.message = "系统异常";
		}
		this.data = null;
	}

	@Override
	public String toString() {
		return "●\t该方法的返回值结果如下:\n" +
				"●\tcode:" + code + '\n' +
				"●\tmessage:" + message + '\n' +
				"●\tdata:\n" + data;
	}

	public Integer getCode() {
		return code;
	}

	public ResultUtil<T> setCode(Integer code) {
		this.code = code;
		return this;
	}

	public String getMessage() {
		return message;
	}

	public ResultUtil<T> setMessage(String message) {
		this.message = message;
		return this;
	}

	public Map<String, T> getData() {
		return data;
	}

	public ResultUtil<T> setData(Map<String, T> data) {
		this.data = data;
		return this;
	}

}

4.3. CustomException(在exception包下)

package com.eshang.my.basket.background.exception;


import edu.gdut.oec.constant.ResultConstant;

/**
 * @author xyx-Eshang
 */
public class CustomException extends Exception {
	/**
	 * 状态码
	 */
	private Integer code;
	/**
	 * 异常信息
	 */
	private String message;

	public CustomException() {
		super();
	}

	public CustomException(Integer code) {
		this.code = code;
		this.message = ResultConstant.messageMap.get(code);
	}

	public CustomException(String methodSignature, String parameterName) {
		this.code = 40001;
		this.message = "执行方法 " + methodSignature + " 时,传入的参数 " + parameterName + " 为空";
	}
	
	@Override
	public String toString() {
		return "CustomException{" +
				"code=" + code +
				", message='" + message + '\'' +
				'}';
	}

	public Integer getCode() {
		return code;
	}

	public CustomException setCode(Integer code) {
		this.code = code;
		return this;
	}

	@Override
	public String getMessage() {
		return message;
	}

	public CustomException setMessage(String message) {
		this.message = message;
		return this;
	}
}


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