最近在尝试使用前后端分离的模式写一个简单的个人博客,遇到接口数据返回结构的问题,在网上查了一圈,发现了一个很好用的方法,在复现的过程中也遇到了不少坑点,特意在这记录下。
创建 SpringBoot 项目
导入下面依赖
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
封装返回结果
新建 result
包,和结果封装相关的类都放在这个包里,创建 ReturnCode
枚举类,因为是演示所以就写了两个返回值,实际使用过程中应该不止这两个类型
@Getter
public enum ReturnCode {
RC200(200, "success"),
RC400(400, "fail");
private final int code;
private final String msg;
ReturnCode(int code, String msg){
this.code = code;
this.msg = msg;
}
}
创建 ResultData
类,作为统一返回类型的封装
@Data
public class ResultData<T> {
private int code;
private String msg;
private T data;
public static <T> ResultData<T> success(T data) {
ResultData<T> resultData = new ResultData<>();
resultData.setCode(ReturnCode.RC200.getCode());
resultData.setMsg(ReturnCode.RC200.getMsg());
resultData.setData(data);
return resultData;
}
public static <T> ResultData<T> fail(int code, String msg) {
ResultData<T> resultData = new ResultData<>();
resultData.setCode(code);
resultData.setMsg(msg);
return resultData;
}
}
实现返回对象的自动封装
每次使用的时候都要 return Result.success()
太麻烦了,可以使用 SpringBoot 实现返回对象的自动封装,创建 ResponseAdvice
,实现 ResponseBodyAdvice<T>
接口
@RestControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
@Autowired
private ObjectMapper objectMapper;
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@SneakyThrows
@Override
public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType,
Class<? extends HttpMessageConverter<?>> aClass,
ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (o instanceof String) {
return objectMapper.writeValueAsString(ResultData.success(o));
}
if (o instanceof ResultData) {
return o;
}
return ResultData.success(o);
}
}
处理异常
这个时候如果发生异常还没办法处理,我们可以由全局异常处理器统一捕获,使用全局异常处理器最大的便利就是写代码时不再需要手写 try...catch
,新建一个 error
包,下包下创建全局异常处理器 RestExceptionHandler
@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.RC400.getCode(), e.getMessage());
}
}
测试
接下来写一个测试控制器,写两个方法测试是否正确工作
@RestController
public class TestController {
@GetMapping("/right")
public String hello() {
return "Hello, SpringBoot!";
}
@GetMapping("/error")
public int error() {
return 9 / 0;
}
}
使用 postman 接口测试工具,分别测试两个接口,可以得到预期的结果。