背景
现有的项目中控制层的返回值总时使用Result进行返回。代码如下:
@GetMapping(value = "/c")
public Result c() {
return Result.OK("c");
}
当所有接口都使用这种方式来返回时,感觉代码的可读性不够,所以通过 @RestControllerAdvice 和 接口ResponseBodyAdvice对代码进行了优化。
实现
基础搭建
项目基于springboot
目录结构
定义返回值
package com.example.universalReturn.common;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class Result<T> {
/**
* 成功标志
*/
private boolean success = true;
/**
* 返回处理消息
*/
private String message = "操作成功!";
/**
* 返回代码
*/
private Integer code = 0;
/**
* 返回数据对象 data
*/
private T result;
/**
* 时间戳
*/
private long timestamp = System.currentTimeMillis();
public static<T> Result<T> OK() {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage("成功");
return r;
}
public static<T> Result<T> OK(T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(data);
return r;
}
public static<T> Result<T> OK(String msg, T data) {
Result<T> r = new Result<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage(msg);
r.setResult(data);
return r;
}
public static Result<Object> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}
public static Result<Object> error(int code, String msg) {
Result<Object> r = new Result<Object>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
public Result<T> error500(String message) {
this.message = message;
this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
this.success = false;
return this;
}
}
定义一个通用的常量
package com.example.universalReturn.common;
public class CommonConstant {
/** {@code 500 Server Error} (HTTP/1.0 - RFC 1945) */
public static final Integer SC_INTERNAL_SERVER_ERROR_500 = 500;
/** {@code 200 OK} (HTTP/1.0 - RFC 1945) */
public static final Integer SC_OK_200 = 200;
}
核心代码
统一处理返回值
package com.example.universalReturn.common;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@RestControllerAdvice
@Slf4j
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
// 1、返回true,则表示执行返回值的处理。后续的代码才会进入beforeBodyWrite方法
return true;
}
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (ObjectUtil.isNull(o)){
return Result.OK();
}
if (o instanceof Result){
return o;
}
if (o instanceof String){
/**
* 当返回类型是String时,消息转换器则是:StringHttpMessageConverter。
*
* 但通过ResponseAdvice返回的是一个Perf2WebRPCResult对象,并不是String类型,
*
* 通过异常堆栈可知,当在执行 getContentLength 时出错,即对该对象强转String发生异常。
*
* 所以将返回的String转为对象之后再转为JSON字符串
*/
return JSONUtil.toJsonStr(Result.OK(o));
}
return Result.OK(o);
}
}
全局异常捕获
package com.example.universalReturn.common;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
public Result<?> handleRuntimeException(Exception e) {
// 用于捕获全局的RuntimeException异常
log.error(e.getMessage(), e);
return Result.error(e.getMessage());
}
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e) {
// 用于捕获全局的Exception异常
log.error(e.getMessage(), e);
return Result.error("操作失败," + e.getMessage());
}
}
常见的场景
返回空值
@GetMapping(value = "/a")
public Integer a() {
return null;
}
处理后的返回值
{
"success": true,
"message": "成功",
"code": 200,
"result": null,
"timestamp": 1665730720705
}
返回字符串
@GetMapping(value = "/b")
public String b() {
return "删除成功";
}
{
"success": true,
"message": "操作成功!",
"code": 200,
"result": "删除成功",
"timestamp": 1665730776941
}
返回统一返回值
@GetMapping(value = "/c")
public Result c() {
return Result.OK("c");
}
{
"success": true,
"message": "操作成功!",
"code": 200,
"result": "c",
"timestamp": 1665730792351
}
返回对象
@GetMapping(value = "/d")
public DemoVo d() {
return new DemoVo("d");
}
{
"success": true,
"message": "操作成功!",
"code": 200,
"result": {
"foo": "d"
},
"timestamp": 1665730798269
}
无返回值
@GetMapping(value = "/e")
public void e() {
log.info("e");
}
{
"success": true,
"message": "成功",
"code": 200,
"result": null,
"timestamp": 1665730803715
}
抛出运行时异常
@GetMapping(value = "/f")
public Object f() {
throw new RuntimeException("返回运行时异常");
}
{
"success": false,
"message": "返回运行时异常",
"code": 500,
"result": null,
"timestamp": 1665717905138
}
抛出异常
@GetMapping(value = "/g")
public Object g() throws Exception {
throw new Exception("返回异常");
}
{
"success": false,
"message": "操作失败,返回异常",
"code": 500,
"result": null,
"timestamp": 1665717909851
}
资源下载
包含demo源码以及对应接口的postman调用文件