栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

统一响应结构体 | 大别山码将

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

统一响应结构体 | 大别山码将

在做前后端分离的项目时,后端通常都会拆分成多个独立的微服务,这时候就会涉及每个服务返回给前端的数据格式问题了
在项目中,应该对响应给前端的 JSON 数据的格式进行统一的规定,在很多响应结构中,一般会包含 code、message、data 三个属性。

code 代表状态码,这个不是 HTTP 的响应状态码,而是后端系统的业务状态码,代表了后端响应给前端的业务状态。

message 代表了后端给前端的响应信息,如果发生异常,或者系统错误,会将错误信息存储在 message 中,响应给前端。

data 代表了后端响应给前端的数据,如果请求获取了数据,会将数据放入 data 域中。

一般返回的数据格式会包括4个部分:
第一部分:请求处理是否成功,
第二部分:服务处理结果编码 code,
第三部分:编码对应的文本信息 message,
第四部分:返回值 date

{
   "result": true,
   "code": 1000,
   "message": "SUCCESS",
   "data": {
       "lantian": 17,
       "qingfen": 16,
       "baiyun": 18
   }
}

对于异常处理情况,我们也需要统一成上面的格式。如果我们在controller中通过try catch来处理异常的话,会出现一个问题就是每个函数里都加一个Try catch,代码会变的很乱。下面我们就通过spring boot的注解来省略掉controller中的try-catch 帮助我们来封装异常信息并返回给前端,这样用户也不会得到一些奇奇怪怪的错误提示。

{
   "result": false,
   "code": 3000,
   "message": "THIS IS AN UNKNOW EXCEPTION",
   "data": null
}

统一响应结构体我的做法是这样子的:
1.定义返回结构体和异常


@NoArgsConstructor
@Slf4j
@Data
public class ResponseData implements Serializable {

   //1.自定义返回值结构信息
   
   private int code;

   
   private String msg;

   
   private Long timestamp;

   
   private T data;

   //2.定义返回值和对应code的信息(code的信息在I18nConstants)
   public ResponseData(int code, String msg, T data) {
   	this.code = code;
   	this.msg = msg;
   	//currentTimeMillis()以毫秒为单位返回当前时间
   	this.timestamp = System.currentTimeMillis();
   	this.data = data;
   }

   //3.自定义异常
   public static  ResponseData ok() {
   	return ok(null);
   }

   public static  ResponseData ok(T data) {
   	return new ResponseData<>(I18nConstants.OK, I18nConstants.OK_MESSAGE, data);
   }

   public static  ResponseData failed() {
   	return failed(null);
   }

   public static  ResponseData failed(T data) {
   	return new ResponseData<>(I18nConstants.FAIL, I18nConstants.FAIL_MESSAGE, data);
   }

   public static  ResponseData failed(int code, String message) {
   	return failed(code, message, null);
   }

   public static  ResponseData failed(int code, String message, T data) {
   	return new ResponseData<>(code, message, data);
   }
}

2.定义code信息

public interface I18nConstants {

    
    int OK = 200;

    
    int FAIL = 500;

    
    int CTRL_ERROR = 501;

    
    int ARGUMENT_ERROR = 503;


    
    String OK_MESSAGE = "成功";

    
    String FAIL_MESSAGE = "服务异常";

    
    String ARGUMENT_ERROR_MESSAGE = "请求参数异常";
}

测试




@RestController
@RequestMapping("/inner/errorTranslate")
@Slf4j
@RequiredArgsConstructor
public class InnerErrorTranslateController {

    private final ErrorTranslateService errorTranslateService;

    
    @GetMapping("/selectList")
    public ResponseData> selectList(ErrorTranslateQuery query) {
        return ResponseData.ok(errorTranslateService.selectList(query));
    }
}

测试结果(OK)

{
 "code": 200,
 "msg": "成功",
 "timestamp": 1641280641936,
 "data": [
   {
     "code": "cn",
     "name": "中文",
     "gmtCreated": 1640293629000
   },
   {
     "code": "en",
     "name": "英文",
     "gmtCreated": 1640293640000
   }
 ]
}

一般我们都进行全局异常处理,免得每次使用都要写一遍ResponseData请求体对象

3.全局异常处理




 
@RestControllerAdvice
@Slf4j
@RequiredArgsConstructor
public class ResultHandlerResolver implements ResponseBodyAdvice {

	
	@ExceptionHandler(Exception.class)
	public ResponseData handleGlobalException(Exception e) {
		log.error("全局异常信息", e);
		return ResponseData.failed(I18nConstants.FAIL_MESSAGE);
	}

	
	@ExceptionHandler(BizException.class)
	public ResponseData handleBusinessException(BizException e) {
		log.error("业务异常", e);
		return ResponseData.failed(e.getCode(), e.getMessage());
	}

	
	@ExceptionHandler({
			HttpRequestMethodNotSupportedException.class,
			HttpMediaTypeNotSupportedException.class,
			HttpMediaTypeNotAcceptableException.class,
			MissingPathVariableException.class,
			MissingServletRequestParameterException.class,
			TypeMismatchException.class,
			HttpMessageNotReadableException.class,
			HttpMessageNotWritableException.class,
			ServletRequestBindingException.class,
			ConversionNotSupportedException.class,
			MissingServletRequestPartException.class,
			AsyncRequestTimeoutException.class
	})
	public ResponseData handleServletException(Exception e) {
		log.error("Controller上层异常", e);
		return ResponseData.failed(I18nConstants.CTRL_ERROR, e.getMessage());
	}


	
	@ExceptionHandler(value = BindException.class)
	public ResponseData handleBindException(BindException e) {
		log.error("参数绑定校验异常", e);
		return wrapperBindingResult(e.getBindingResult());
	}

	
	@ExceptionHandler(value = MethodArgumentNotValidException.class)
	public ResponseData handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
		log.error("参数绑定校验异常", e);
		return wrapperBindingResult(e.getBindingResult());
	}

	
	@ExceptionHandler(value = ConstraintViolationException.class)
	public ResponseData handleConstraintViolationException(ConstraintViolationException e) {
		log.error("参数绑定校验异常", e);

		// stringBuilder拼接
		StringBuilder stringBuilder = new StringBuilder();

		// 循环处理参数绑定校验异常信息
		for (ConstraintViolation constraintViolation : e.getConstraintViolations()) {
			stringBuilder.append(constraintViolation.getMessage()).append(",");
		}

		// 错误信息
		String errorMessage = stringBuilder.toString();

		// 去除最后的逗号
		if (errorMessage.length() > 1) {
			errorMessage = StringUtils.removeEnd(errorMessage, ",");
		}

		return ResponseData.failed(I18nConstants.ARGUMENT_ERROR, errorMessage);
	}

	
	private ResponseData wrapperBindingResult(BindingResult bindingResult) {
		StringBuilder msg = new StringBuilder();
		for (ObjectError error : bindingResult.getAllErrors()) {
			msg.append(", ");
			msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
		}

		return ResponseData.failed(I18nConstants.ARGUMENT_ERROR, msg.substring(2));
	}

	@Override
	public boolean supports(MethodParameter methodParameter, Class aClass) {
		return true;
	}


	@Override
	public Object beforeBodyWrite(Object obj, MethodParameter methodParameter, MediaType mediaType, Class> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
		if(obj instanceof ResponseData || obj instanceof File) {
			return obj;
		}
		return ResponseData.ok(obj);
	}
}
 

有了全局异常处理后,我们就可以专注写自己的业务代码了




@RestController
@RequestMapping("/inner/errorTranslate")
@Slf4j
@RequiredArgsConstructor
public class InnerErrorTranslateController {

    private final ErrorTranslateService errorTranslateService;

    
    @GetMapping("/selectList")
    public List selectList(ErrorTranslateQuery query) {
        return errorTranslateService.selectList(query);
    }
}

测试结果(FAIL)

{
  "code": 500,
  "msg": "语言编码已存在",
  "timestamp": 1641283519114,
  "data": null
}
转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号