本文目录导读:

在Java后端开发中,统一返回格式的核心目的是让前端能够以统一的结构解析后端响应,便于处理成功、失败、异常等不同情况,下面我会介绍一个经典的封装案例,涵盖泛型、静态工厂方法、枚举状态码等最佳实践。
核心实体类封装
定义状态码枚举(强烈推荐)
public enum ResultCode {
SUCCESS(200, "操作成功"),
FAILED(500, "操作失败"),
VALIDATE_FAILED(400, "参数校验失败"),
UNAUTHORIZED(401, "未登录或token已过期"),
FORBIDDEN(403, "没有相关权限"),
NOT_FOUND(404, "资源不存在"),
SERVER_ERROR(500, "服务器内部错误");
private final int code;
private final String message;
ResultCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}
统一返回类 ApiResult<T>
import lombok.Data;
import java.io.Serializable;
@Data
public class ApiResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private String message;
private T data;
private long timestamp; // 可选,建议加上方便排查
// 私有构造器,防止外部直接实例化
private ApiResult() {
this.timestamp = System.currentTimeMillis();
}
// ========== 成功响应(无数据返回) ==========
public static <T> ApiResult<T> success() {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.SUCCESS.getCode();
result.message = ResultCode.SUCCESS.getMessage();
return result;
}
// ========== 成功响应(有数据) ==========
public static <T> ApiResult<T> success(T data) {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.SUCCESS.getCode();
result.message = ResultCode.SUCCESS.getMessage();
result.data = data;
return result;
}
// ========== 成功响应(自定义消息) ==========
public static <T> ApiResult<T> success(String message, T data) {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.SUCCESS.getCode();
result.message = message;
result.data = data;
return result;
}
// ========== 失败响应(默认500) ==========
public static <T> ApiResult<T> failed() {
return failed(ResultCode.FAILED);
}
public static <T> ApiResult<T> failed(String message) {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.FAILED.getCode();
result.message = message;
return result;
}
// ========== 自定义状态码失败响应 ==========
public static <T> ApiResult<T> failed(ResultCode resultCode) {
ApiResult<T> result = new ApiResult<>();
result.code = resultCode.getCode();
result.message = resultCode.getMessage();
return result;
}
public static <T> ApiResult<T> failed(ResultCode resultCode, String message) {
ApiResult<T> result = new ApiResult<>();
result.code = resultCode.getCode();
result.message = message;
return result;
}
// ========== 便捷方法:参数校验失败 ==========
public static <T> ApiResult<T> validateFailed(String message) {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.VALIDATE_FAILED.getCode();
result.message = message;
return result;
}
// ========== 便捷方法:未授权 ==========
public static <T> ApiResult<T> unauthorized(String message) {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.UNAUTHORIZED.getCode();
result.message = message;
return result;
}
// ========== 便捷方法:无权限 ==========
public static <T> ApiResult<T> forbidden(String message) {
ApiResult<T> result = new ApiResult<>();
result.code = ResultCode.FORBIDDEN.getCode();
result.message = message;
return result;
}
// ========== 链式调用支持(可选) ==========
public ApiResult<T> code(int code) {
this.code = code;
return this;
}
public ApiResult<T> message(String message) {
this.message = message;
return this;
}
public ApiResult<T> data(T data) {
this.data = data;
return this;
}
}
使用示例
Controller 中直接使用
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public ApiResult<User> getUser(@PathVariable Long id) {
// 模拟查询
User user = userService.getById(id);
if (user == null) {
return ApiResult.failed(ResultCode.NOT_FOUND, "用户不存在");
}
return ApiResult.success(user);
}
@PostMapping
public ApiResult<Void> createUser(@Valid @RequestBody UserCreateDTO dto) {
// 参数校验失败会由全局异常处理捕获,这里假设业务正常
userService.create(dto);
return ApiResult.success("创建成功", null);
}
}
配合全局异常处理(Spring Boot)
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
// 处理参数校验异常
@ExceptionHandler(MethodArgumentNotValidException.class)
public ApiResult<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.joining("; "));
return ApiResult.validateFailed(message);
}
// 处理业务异常
@ExceptionHandler(BusinessException.class)
public ApiResult<Void> handleBusinessException(BusinessException e) {
return ApiResult.failed(e.getCode(), e.getMessage());
}
// 处理其他未捕获异常
@ExceptionHandler(Exception.class)
public ApiResult<Void> handleException(Exception e) {
log.error("系统异常", e);
return ApiResult.failed(ResultCode.SERVER_ERROR, "系统繁忙,请稍后再试");
}
}
自定义业务异常类
public class BusinessException extends RuntimeException {
private int code;
private String message;
public BusinessException(ResultCode resultCode) {
super(resultCode.getMessage());
this.code = resultCode.getCode();
this.message = resultCode.getMessage();
}
public BusinessException(ResultCode resultCode, String message) {
super(message);
this.code = resultCode.getCode();
this.message = message;
}
// getter/setter
}
前端收到的 JSON 示例
// 成功响应(有数据)
{
"code": 200,
"message": "操作成功",
"data": {
"id": 1,
"username": "admin",
"email": "admin@example.com"
},
"timestamp": 1691234567890
}
// 成功响应(无数据)
{
"code": 200,
"message": "操作成功",
"data": null,
"timestamp": 1691234567890
}
// 失败响应
{
"code": 400,
"message": "用户名不能为空",
"data": null,
"timestamp": 1691234567890
}
// 未授权
{
"code": 401,
"message": "未登录或token已过期",
"data": null,
"timestamp": 1691234567890
}
// 服务器内部错误
{
"code": 500,
"message": "系统繁忙,请稍后再试",
"data": null,
"timestamp": 1691234567890
}
最佳实践建议
不要直接在返回对象上使用 @Data
- 如果使用 Lombok
@Data,记得加上@Accessors(chain = true)以支持链式调用。
统一时间戳格式
- 建议在
ApiResult的构造器中设置timestamp,或者使用Instant.now().toEpochMilli()。
避免返回敏感信息
- 在
data字段中,使用 DTO(数据传输对象)而非实体类,避免暴露密码等敏感字段。
分页查询统一包装
@GetMapping("/page")
public ApiResult<PageResult<User>> getUserPage(@RequestParam int page, @RequestParam int size) {
PageResult<User> pageResult = userService.getPage(page, size);
return ApiResult.success(pageResult);
}
// PageResult 示例
@Data
public class PageResult<T> {
private List<T> list;
private long total;
private int page;
private int size;
}
与 Spring 的 ResponseEntity 结合
如果你需要控制 HTTP 状态码(201 Created),可以这样:
@PostMapping
public ResponseEntity<ApiResult<User>> createUser(@RequestBody User user) {
User createdUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResult.success("创建成功", createdUser));
}
| 特性 | 说明 |
|---|---|
| 类型安全 | 使用泛型 T 保证 data 字段的编译时类型检查 |
| 易用性 | 静态工厂方法(success()、failed())让返回代码简洁 |
| 可扩展 | 枚举 ResultCode 统一管理状态码,新增状态码只需添加枚举项 |
| 异常处理 | 全局异常拦截器 @RestControllerAdvice 自动捕获异常并转换 |
这种封装方案在实际项目中经过大量验证,适用于微服务、单体应用,并且可以无缝集成到 Spring Boot 生态中。