springboot学习之十一(统一返回结果)
SpringBoot统一返回结果
在实际开发中,为了降低开发人员之间的沟通成本,一般返回结果会定义成一个统一格式,具体的格式根据实际开发业务不同有所区别,但至少包括三要素:
code状态码:由后端统一定义各种返回结果的状态码
message 描述:本次接口调用的结果描述
data 数据:本次返回的数据。
{ "code": 200, "msg": "操作成功", "data": "Hello World,test123", }
1 基础实现方式
1.1 定义状态码
public enum ReturnCode { SUCCESS(200, "操作成功"), NEED_LOGIN(401, "需要登录后操作"), NO_OPERATOR_AUTH(403, "无权限操作"), TOKEN_ILL(404,"token非法"), SYSTEM_ERROR(500, "出现错误"), USERNAME_EXIST(501, "用户名已经存在"), PHOMENUMBER_EXIST(502, "手机号已存在"), EMAIL_EXIST(503, "邮箱已存在"), REQUEST_USERNAME(504, "必须填写用户名"), LOGIN_ERROR(505, "用户名或密码错误"); // 自定义状态码 private final int code; // 自定义描述 private final String msg; ReturnCode(int code, String errorMessage) { this.code = code; this.msg = errorMessage; } public int getCode() { return this.code; } public String getMsg() { return this.msg; } }
1.2 定义返回对象
@Data public class ResultData<T> { /** * 响应编码 */ private Integer code; /** * 响应信息 */ private String msg; /** * 响应数据 */ private T data; /** * 接口请求时间 */ private long timestamp ; public ResultData(){ this.timestamp = System.currentTimeMillis(); } public ResultData(Integer code, String msg){ this.code=code; this.msg=msg; } public ResultData(Integer code, String msg, T data){ this.code=code; this.msg=msg; this.data=data; } public static ResultData success(){ return ResultData.success(ReturnCode.SUCCESS.getCode(),ReturnCode.SUCCESS.getMsg()); } public static ResultData success(int code, String msg){ ResultData<Object> resultData = new ResultData<>(); resultData.setCode(code); resultData.setMsg(msg); return resultData; } public static ResultData success(Object data){ ResultData<Object> resultData = new ResultData<>(ReturnCode.SUCCESS.getCode(),ReturnCode.SUCCESS.getMsg()); if (Objects.nonNull(data)){ resultData.setData(data); } return resultData; } public static ResultData error(int code, String msg){ ResultData<Object> resultData = new ResultData<>(code,msg); return resultData; } public static ResultData error(ReturnCode returnCode){ ResultData<Object> resultData = new ResultData<>(returnCode.getCode(), returnCode.getMsg()); return resultData; } }
1.3 统一返回格式
@GetMapping("/getTest") public ResultData<String> getTest(){ return ResultData.success("Hello world!"); }
返回结果如下所示:
{ "code": 200, "msg": "操作成功", "data": "Hello world!", "timestamp": 1639013414924 }
到这里,一个统一格式的返回结果已经实现,接口的返回类型为ResultData,但每写一个接口,就要调用ResultData.success()
对结果进行包装,重复劳动,浪费体力,所以这个在实际工作中一般用的比较少。
2 高级实现方式
我们只需要借助SpringBoot 提供的ResponseBodyAdvice
即可。
ResponseBodyAdvice
可以在注解@ResponseBody
将返回值处理成相应格式之前操作返回值。实现这个接口即可完成相应操作。可用于对response 数据的一些统一封装或者加密等操作
**ResponseBodyAdvice的作用:**拦截Controller方法的返回值,统一处理返回值/响应体,一般用来统一返回格式,加解密,签名等等。
所以@ResponseBodyAdvice只对@ResponseBody注解的方法有效,如果一个控制器没有加@ResponseBody那还是按正常的找view走。
2.1 ResponseBodyAdvice返回
@RestControllerAdvice(basePackages = "org.tesheng.controller") public class ResponseAdvice implements ResponseBodyAdvice { @Autowired private ObjectMapper objectMapper; @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @SneakyThrows @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if(body instanceof String){ // 如果Controller直接返回String的话,SpringBoot是直接返回,故我们需要手动转换成json。 return objectMapper.writeValueAsString(ResultData.success(body)); } if(body instanceof ResultData){ // 如果返回的结果是ResultData对象,直接返回即可。 return body; } return ResultData.success(body); } }
2.2 全局异常处理器
@Slf4j @RestControllerAdvice public class RestExceptionHandler { @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResultData<String> exception(Exception e){ log.error("全局异常信息 ex={}", e.getMessage(), e); return ResultData.fail(ReturnCode.SYSTEM_ERROR.getCode(), e.getMessage()); } }
@RestControllerAdvice,RestController的增强类,可用于实现全局异常处理器
@ExceptionHandler,统一处理某一类异常,从而减少代码重复率和复杂度,比如要获取自定义异常可以 @ExceptionHandler(BusinessException.class)
@ResponseStatus指定客户端收到的http状态码
2.3 体验效果
@GetMapping("/wrong") public int error(){ int i = 9/0; return i; }
返回结果如下:
{ "code": 500, "msg": "/ by zero", "data": null, "timestamp": 1639014045820 }
---------------------
处理自定义错误
1.自定义错误类:
public class BizException extends RuntimeException { public BizException(String msg, Throwable cause) { super(msg, cause); } public BizException(String msg) { super(msg); } }
2.全局异常添加自定义类
@RestControllerAdvice @Slf4j public class GlobalException { @ExceptionHandler(value = {NullPointerException.class}) public String nullPointerException(Model model,Exception e){ model.addAttribute("msg",e.getMessage()); log.info(e.getMessage()); return "error2"; } @ExceptionHandler(value = {ArithmeticException.class}) public ResultData<String> arithmeticException(Exception e){ log.info(e.getMessage()); return ResultData.error(ReturnCode.SYSTEM_ERROR.getCode(), e.getMessage()); } @ExceptionHandler(value = {BizException.class}) public ResultData<String> bizException(Exception e){ log.info(e.getMessage()); return ResultData.error(ReturnCode.SYSTEM_ERROR.getCode(), e.getMessage()); } @ExceptionHandler(value = {Exception.class}) public ResultData<String> exception(Exception e){ log.info(e.getMessage()); return ResultData.error(ReturnCode.SYSTEM_ERROR.getCode(), e.getMessage()); } }
3.写错误
@RequestMapping("index03") public String index03(){ if(true){ throw new BizException("业务忙"); } return "index03"; }
转:https://blog.csdn.net/asxyxxx/article/details/121806762
https://www.cnblogs.com/mxh-java/p/14319420.html