关键是要走稳“资源定义→协议约定→逻辑分层→安全兜底”链路:一、以资源为中心设计uri和http动词;二、controller仅接收校验与响应组装,service专注业务语义与事务,repository只管数据存取;三、用全局异常处理、前置校验注解、trace日志和swagger保障可观测性;四、跨域需显式配置、上传须多重校验、多环境配置严格分离。

直接上手做一个能跑通、可维护、贴合真实业务的 REST 接口项目,关键不是堆功能,而是把“资源定义→协议约定→逻辑分层→安全兜底”这条链路走稳。下面按实战节奏拆解四个核心环节,每个都对应开发中真正卡点的地方。
一、先定资源和动词,别急着写 Controller
REST 不是给方法起个带 /api/ 前缀的名字。比如做图书管理,别写 /getBookById?id=123,而要明确:
-
资源是什么:Book 是核心资源,URI 就是
/books和/books/{id} -
操作怎么映射:查列表用
GET /books,新增用POST /books,改单本用PUT /books/{id},软删用PATCH /books/{id}/status -
状态码要说话:创建成功返回
201 Created并带Location头;找不到返回404 Not Found;参数错返回400 Bad Request,不笼统甩 500
二、分层不能只图结构好看,得守住边界
很多项目分了 controller/service/repository,但 service 层塞满 HTTP 工具类、DTO 转 VO 全在 controller 里硬写——这等于没分。实战中建议这样划界:
-
Controller 层只做三件事:接收请求(含校验注解
@Valid)、调用 Service、组装响应(用统一的ApiResponse<t></t>包裹) -
Service 层专注业务语义:比如 “下单” 不是简单 insert 订单表,而是包含库存预占、价格重算、优惠券核销、生成订单号等子动作;用
@Transactional控制事务边界,用ApplicationEvent解耦后续动作(如发短信、更新积分) -
Repository 只管数据存取:JPA 的
CrudRepository或 MyBatis 的 Mapper 接口,不出现 SQL 拼接、手动事务或日志打印
三、让接口“活”起来,得靠这几样实打实的支撑
一个接口上线后不出问题,靠的不是代码写得漂亮,而是有配套机制兜着:
-
全局异常处理:用
@RestControllerAdvice捕获ConstraintViolationException(参数校验失败)、EntityNotFoundException(查不到)、自定义业务异常(如InsufficientStockException),统一转成标准 JSON 响应 -
基础校验必须前置:ID 是否为空、手机号是否合规、文件大小是否超限——这些别等进 service 才判断,用
@NotBlank、@Pattern、@Max等注解 +@Valid触发,controller 层直接拦截 -
日志和可观测性:每个请求打一条 trace 日志(含 request ID、耗时、路径、状态码);用
@Timed(Micrometer)暴露接口 P95 耗时;Swagger 或 Springdoc 生成文档并同步到测试环境
四、上线前必过三关:跨域、安全、环境隔离
本地跑通 ≠ 前端能调通 ≠ 生产能扛住。这三个坑最常被忽略:
-
跨域配置别只写 * :生产环境必须显式声明
allowedOrigins(如https://admin.example.com),禁用allowCredentials = true时不要配*,否则浏览器会拒绝 -
敏感操作加防护:上传接口必须校验文件后缀 + MIME 类型 + 文件头魔数;用户删除账号需二次确认(前端传 token 或后端要求 POST 到
/users/{id}/deactivate);所有修改类接口记录操作人、IP、时间 -
多环境配置分离:数据库地址、微信公众号 token、OSS 密钥等,用
application-dev.yml/application-prod.yml隔离;启动时通过--spring.profiles.active=prod指定,避免测试库连上生产表











