You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于SpringBoot 2.0.0开发RestAPI:如何统一自定义接口响应?

嘿,我来帮你搞定Spring Boot 2.0.0里统一自定义API响应的需求!核心思路是定义统一的响应体结构,再通过全局拦截包装实现无侵入式的统一处理,下面一步步来:

1. 定义统一响应实体类

首先创建一个通用的响应体,用来封装所有接口的返回结果,包含状态码、提示消息、业务数据和分页信息(按需):

import lombok.Data;
import org.springframework.data.domain.Page;
import java.util.List;

@Data
public class ApiResponse<T> {
    // 自定义业务状态码(比如200=成功,400=参数错误)
    private int code;
    // 响应提示消息
    private String message;
    // 业务数据(支持单个对象/集合)
    private T data;
    // 分页信息(仅分页接口返回)
    private Pagination pagination;

    // 快速创建成功响应的静态方法
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setCode(200);
        response.setMessage("请求成功");
        response.setData(data);
        
        // 如果返回的是Spring Data的Page对象,自动填充分页信息
        if (data instanceof Page) {
            Page<?> page = (Page<?>) data;
            Pagination pagination = new Pagination();
            pagination.setPageNumber(page.getNumber());
            pagination.setPageSize(page.getSize());
            pagination.setTotalElements(page.getTotalElements());
            pagination.setTotalPages(page.getTotalPages());
            pagination.setFirst(page.isFirst());
            pagination.setLast(page.isLast());
            response.setPagination(pagination);
        }
        return response;
    }

    // 带自定义消息的成功响应
    public static <T> ApiResponse<T> success(String message, T data) {
        ApiResponse<T> response = success(data);
        response.setMessage(message);
        return response;
    }

    // 快速创建错误响应的静态方法
    public static <T> ApiResponse<T> error(int code, String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setCode(code);
        response.setMessage(message);
        response.setData(null);
        response.setPagination(null);
        return response;
    }

    // 内部类:封装分页详情
    @Data
    public static class Pagination {
        private int pageNumber;
        private int pageSize;
        private long totalElements;
        private int totalPages;
        private boolean first;
        private boolean last;
    }
}

注:如果不用Lombok的@Data,手动编写getter/setter即可。

2. 全局响应拦截包装(无侵入式)

通过Spring的ResponseBodyAdvice实现全局响应包装,不用修改现有Controller的返回值:

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 可以在这里过滤不需要包装的接口(比如静态资源),默认对所有接口生效
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        // 已经是ApiResponse类型,直接返回
        if (body instanceof ApiResponse) {
            return body;
        }
        // 返回null的情况,包装成空数据的成功响应
        if (body == null) {
            return ApiResponse.success(null);
        }
        // 其他类型统一包装成成功响应
        return ApiResponse.success(body);
    }
}
3. 全局异常统一处理

为了让异常也返回统一格式,添加全局异常处理器:

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalExceptionHandler {

    // 处理自定义业务异常
    @ExceptionHandler(CustomException.class)
    public ApiResponse<Void> handleCustomException(CustomException e) {
        return ApiResponse.error(e.getCode(), e.getMessage());
    }

    // 处理通用系统异常
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiResponse<Void> handleSystemException(Exception e) {
        // 生产环境建议隐藏具体异常信息,返回通用提示
        return ApiResponse.error(500, "服务器内部错误");
    }

    // 处理参数校验异常
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiResponse<Void> handleParamException(MethodArgumentNotValidException e) {
        String errorMsg = e.getBindingResult().getFieldErrors().stream()
                .map(err -> err.getField() + ": " + err.getDefaultMessage())
                .findFirst()
                .orElse("参数格式错误");
        return ApiResponse.error(400, errorMsg);
    }
}

对应的自定义业务异常类:

public class CustomException extends RuntimeException {
    private int code;

    public CustomException(int code, String message) {
        super(message);
        this.code = code;
    }

    public int getCode() {
        return code;
    }
}
4. 现有Controller无需修改

你的原Controller可以保持不变,比如:

@RestController
@RequestMapping("/api/contacts")
public class ContactController {

    @Autowired
    private ContactRepository contactRepository;

    @GetMapping
    public Page<Contact> getContacts(Pageable pageable) {
        return contactRepository.findAll(pageable);
    }
}
最终效果

请求http://localhost:8080/api/contacts会返回如下统一格式的响应:

{
  "code": 200,
  "message": "请求成功",
  "data": [
    {
      "id": 10,
      "name": "raj"
    }
  ],
  "pagination": {
    "pageNumber": 0,
    "pageSize": 10,
    "totalElements": 1,
    "totalPages": 1,
    "first": true,
    "last": true
  }
}

你可以根据业务需求调整ApiResponse的字段(比如添加时间戳、请求ID等),或者优化分页信息的内容。

内容的提问来源于stack exchange,提问作者thirumalairaj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:27:33