尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

用SpringBoot写API接口,如何设计返回结构更清晰

用SpringBoot写API接口,如何设计返回结构更清晰 上个月接手一个维护了三年的老项目光是理清各个接口的返回格式就花了两天。同一个用户信息接口有的返回{code:0,data:{...}}有的返回{status:success,result:{...}}还有的直接返回裸数据。前端同事吐槽说每次调接口都像开盲盒——不知道打开会是惊喜还是惊吓。在前后端分离的开发模式下后端返回给前端的是JSON数据。如果每个接口的返回结构都不一样前端就需要为每个接口单独写解析逻辑联调效率极低。一套清晰、统一的返回结构是前后端高效协作的第一道桥梁。第一步定义统一的返回体一个健壮的响应体至少包含三个核心字段状态码、提示信息、业务数据。用泛型类来封装是最常见的做法java复制下载Data public class ResultT { private Integer code; // 状态码 private String message; // 提示信息 private T data; // 业务数据 private Long timestamp; // 时间戳可选 public Result() { this.timestamp System.currentTimeMillis(); } public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(操作成功); result.setData(data); return result; } public static T ResultT fail(Integer code, String message) { ResultT result new Result(); result.setCode(code); result.setMessage(message); return result; } }这样无论接口成功还是失败返回的JSON结构都是固定的——code判断成败message说明原因data承载数据。第二步用枚举管理状态码状态码散落在代码各处是混乱的根源。应该用枚举统一管理java复制下载Getter public enum ResultCode { SUCCESS(200, 操作成功), PARAM_ERROR(400, 参数错误), UNAUTHORIZED(401, 未登录), FORBIDDEN(403, 无权限), NOT_FOUND(404, 资源不存在), SERVER_ERROR(500, 系统繁忙请稍后再试); private final Integer code; private final String message; ResultCode(Integer code, String message) { this.code code; this.message message; } }这样做的好处是新增错误码只需在枚举中加一行全局生效。前端也能根据统一的code码做全局拦截处理。第三步全局异常处理让错误响应也统一很多项目只在成功时返回统一格式一旦抛异常就直接把堆栈信息扔给前端。这不仅暴露系统内部结构还让前端无法统一处理错误。用RestControllerAdvice配合ExceptionHandler可以统一拦截所有异常返回标准格式java复制下载RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(BusinessException.class) public Result? handleBusinessException(BusinessException e) { return Result.fail(e.getCode(), e.getMessage()); } ExceptionHandler(MethodArgumentNotValidException.class) public Result? handleValidationException(MethodArgumentNotValidException e) { String msg e.getBindingResult().getFieldError().getDefaultMessage(); return Result.fail(ResultCode.PARAM_ERROR.getCode(), msg); } ExceptionHandler(Exception.class) public Result? handleException(Exception e) { log.error(系统异常, e); return Result.fail(ResultCode.SERVER_ERROR.getCode(), ResultCode.SERVER_ERROR.getMessage()); } }这样一来Controller层再也不需要写try-catch只管正常业务流程异常由全局处理器统一兜底。第四步用ResponseBodyAdvice实现自动包装如果每个Controller方法都要手动return Result.success(data)代码依然冗余。SpringBoot提供了ResponseBodyAdvice接口可以在响应写出前统一拦截和包装java复制下载ControllerAdvice public class ResponseAdvice implements ResponseBodyAdviceObject { Override public boolean supports(MethodParameter returnType, Class? extends HttpMessageConverter? converterType) { return true; } Override public Object beforeBodyWrite(Object body, ...) { // 如果已经是Result类型直接返回 if (body instanceof Result) { return body; } // 自动包装成统一格式 return Result.success(body); } }配合一个IgnoreWrapper注解还可以灵活控制哪些接口跳过包装。从此Controller方法只需返回业务数据本身干净又清爽。两个容易忽略的设计细节第一HTTP状态码和业务状态码要不要分开一种做法是让HTTP状态码承载业务语义200成功、400参数错误、500系统错误另一种是HTTP状态码统一返回200用自定义code区分业务结果。我更推荐前者——充分利用HTTP协议本身的语义前端可以依据HTTP状态码做统一的错误拦截业务code则承载更细致的错误分类。第二空值怎么处理返回的JSON里null字段该不该出现建议在全局配置中统一处理空值序列化要么所有null都保留前端好判断要么所有null都省略数据更精简切忌时而保留时而省略。接口返回结构的设计看似简单实则考验的是对团队协作和长期维护的理解。一套清晰的返回结构能让前端同事少问一百句这个字段啥意思能让新接手项目的同事少花两天理清格式能让系统在迭代中始终保持一致。你的接口返回结构设计清楚了吗
返回列表