在应用开发中,为了提高开发效率通常需要对后端的响应结果定义统一的规范,即无论失败还是成功,返回的响应信息都应该具备固定的样式。

本文即介绍如何在 Spring Boot 接口开发中实现统一的响应结果规范。

1. 前提准备

在开始前先准备实体类 ResponseData 作用统一返回的结果对象。

ResponseData 中定义了 success() 与 failed() 两个方法分别对应成功请求与程序异常。

@Data
public class ResponseData {

    private int code;

    private String message;

    private Object data;

    public static ResponseData success(Object data) {
        ResponseData response = new ResponseData();
        response.setCode(200);
        response.setMessage("success");
        response.setData(data);
        return response;
    }

    public static ResponseData failed(String message) {
        ResponseData response = new ResponseData();
        response.setCode(500);
        response.setMessage(message);
        response.setData(null);
        return response;
    }
}

2. 异常处理

针对程序运行中的异常我们需要单独进行处理,新建处理类 ExceptionHandle 并通过 @RestControllerAdvice 与 @ExceptionHandler 注解实现异常的捕获监听处理。

@RestControllerAdvice
public class ExceptionHandle {

    /**
     * 监听异常请求并处理返回
     */
    @ExceptionHandler(Exception.class)
    public ResponseData handleNotFoundException(Exception ex) {
        return ResponseData.failed(ex.getMessage());
    }
}

3. 结果封装

通过 @RestControllerAdvice 注解与 ResponseBodyAdvice 接口我们接口实现返回结果的统一封装。

ResponseBodyAdvice 接口类中存在两个接口方法:

  • supports():用于控制是否启用。
  • beforeBodyWrite():用于控制具体实现逻辑。

java

@RestControllerAdvice
public class ResponseHandle 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) {
        ResponseData responseData;
        responseData = ResponseData.success(o);
        if (o instanceof String) {
            // String 类型需要转为 Json 格式返回
            return objectMapper.writeValueAsString(responseData);
        } else if (o instanceof ResponseData) {
            // 异常处理中已经包了一层这里直接返回
            return o;
        } else {
            return responseData;
        }
    }
}

4. 示例演示

完成上述配置之后新建如下几个测试接口:

@RestController
@RequestMapping("/api/seal")
public class BasicController {

    @GetMapping("demo1")
    public int demo1() {
        return 1;
    }

    @GetMapping("demo2")
    public boolean demo2() {
        return true;
    }

    @GetMapping("demo3")
    public String demo3() {
        return "Hello";
    }

    @GetMapping("demo4")
    public User demo4() {
        return new User("123", "Alex", "Test user");
    }

    @GetMapping("demo5")
    public List<User> demo5() {
        List<User> userList = new ArrayList<>();
        for (int i = 1; i < 3; i++) {
            userList.add(new User(i + "", "user-" + i, "Test user " + i));
        }
        return userList;
    }
    
    @GetMapping("demo6")
    public String demo6() throws DemoException {
        throw new DemoException("Program went wrong!");
    }
}

上述示例接口的请求效果如下:

基本类型
复杂对象
异常结果