接口文档不是"写给别人看的说明书",而是前后端协作、错误处理和接口测试的共同依据。本文将梳理接口文档的组成、统一响应格式、状态码设计,以及如何使用 Swagger 自动生成接口文档。
一、接口文档解决什么问题?
在微服务项目中,前端需要知道:
- 接口地址是什么;
- 应该使用 GET、POST、PUT 还是 DELETE;
- 请求参数放在路径、查询参数、请求头还是请求体中;
- 请求成功或失败时,返回什么数据;
- 不同错误码分别代表什么含义。
这些信息如果只依赖口头沟通,很容易出现前后端理解不一致。因此,接口文档通常由后端开发人员编写,并经过前后端讨论后确定。
一份完整的接口文档通常包含:
- 接口名称和功能说明;
- 请求地址;
- 请求方法;
- 请求参数;
- 响应数据结构;
- 成功和失败示例;
- 状态码及其含义。
统一的接口格式能够减少前端解析逻辑,简化错误处理,也方便后续维护和新成员接手项目。
二、统一响应格式:让所有接口"说同一种语言"
如果每个接口都返回不同格式的数据,前端就必须针对每个接口编写特殊处理逻辑。
例如,有的接口返回:
java
{
"success": true,
"result": {}
}
另一个接口却返回:
java
{
"status": 200,
"message": "success",
"data": {}
}
这种差异会增加开发和维护成本。
更常见的做法是定义统一响应对象:
java
public class Result<T> {
/**
* 消息状态码
*/
private int code;
/**
* 消息内容
*/
private String msg;
/**
* 数据对象
*/
private T data;
}
其中:
code:表示操作结果;msg:给出可读的提示信息;data:真正的业务数据,使用泛型T表示。
例如,查询用户详情时可以返回:
java
{
"code": 1000,
"msg": "操作成功",
"data": {
"id": 1,
"username": "admin"
}
}
删除操作没有具体返回数据时,可以使用:
Result<Void>
这样既保持接口格式统一,也能准确表达"操作成功但没有数据返回"的含义。
三、HTTP 状态码和业务状态码有什么区别?
1. HTTP 状态码
HTTP 状态码反映的是请求在协议层面的处理结果:
| 状态码范围 | 含义 |
|---|---|
| 1XX | 信息响应,请求还需要继续处理 |
| 2XX | 请求成功 |
| 3XX | 需要重定向 |
| 4XX | 客户端请求错误 |
| 5XX | 服务端错误 |
但是,HTTP 状态码并不能覆盖所有业务场景。
例如,以下情况都可能返回 HTTP 200:
- 用户名已经存在;
- 用户不存在;
- 参数校验失败;
- 用户没有权限;
- 操作执行失败。
因此,系统还需要定义业务层面的状态码。
2. 自定义业务状态码
可以通过枚举统一管理业务状态码:
java
@Getter
@AllArgsConstructor
public enum ResultCode {
// 操作成功
SUCCESS(1000, "操作成功"),
// 服务器内部错误
ERROR(2000, "服务繁忙,请稍后重试"),
// 操作失败,但服务器本身没有异常
FAILED(3000, "操作失败"),
FAILED_UNAUTHORIZED(3001, "未授权"),
FAILED_PARAMS_VALIDATE(3002, "参数校验失败"),
FAILED_NOT_EXISTS(3003, "资源不存在"),
FAILED_ALREADY_EXISTS(3004, "资源已存在"),
// 用户相关错误
FAILED_USER_EXISTS(3101, "用户已存在"),
FAILED_USER_NOT_EXISTS(3102, "用户不存在"),
FAILED_LOGIN(3103, "用户名或密码错误"),
FAILED_USER_BANNED(3104, "您已被列入黑名单,请联系管理员");
private final int code;
private final String msg;
}
枚举类中的succes(1000,"操作成功")这是基于有参的构造方法来创建一个枚举对象, 后端项目中要获取状态码和信息时可以通过getCode 和getMsg来获取.所以我们要为枚举类的开头上添加@Getter 和@AllArgsConstructor 这两个注解.以便于值的获取和对象的成功创建
这种设计中,HTTP 状态码和业务状态码各自承担不同职责:
- HTTP 状态码:描述网络请求是否正常完成;
- 业务状态码:描述具体业务操作的结果。
例如:
{
"code": 3103,
"msg": "用户名或密码错误",
"data": null
}
这比单纯返回"请求失败"更有价值,前端可以根据 code 展示不同提示。
需要注意的是,服务端发生异常时,不应该直接把数据库异常、堆栈信息等底层内容返回给用户,而应该返回统一、友好的错误提示。
四、为什么要使用 Swagger?
手工维护接口文档存在一个明显问题:代码修改后,文档很容易忘记同步。
Swagger,也就是 OpenAPI 生态中的常用接口文档工具,可以解决两个问题:
- 根据代码和注解自动生成接口文档;
- 在文档页面中直接测试接口。
这样,接口发生变化时,文档可以随着代码更新,降低文档过期的风险。
在 Spring Boot 项目中,可以引入 SpringDoc:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.2.0</version>
</dependency>
在公共模块中统一配置 OpenAPI:
java
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI openAPI() {
return new OpenAPI()
.info(new Info()
.title("在线 OJ 系统")
.description("在线 OJ 系统接口文档")
.version("v1"));
}
}
在微服务项目中,可以将 Swagger 配置单独封装成公共模块,例如:
oj-common
└── oj-common-swagger
具体业务服务只需要引入该模块即可:
<dependency>
<groupId>com.bite</groupId>
<artifactId>oj-common-swagger</artifactId>
<version>${oj.common.swagger.version}</version>
</dependency>
这里我把依赖的版本号的控制全部写在了oj-common-swagger 的原始父工程oj-amadeus的pom.xml文件中

为什么这样做呢?因为swagger是一个公共模块, 我们不希望在调整其版本的时候需要把所在模块和使用其依赖的moudle都进行调整,避免牵一发而动全身的麻烦
如果该公共模块需要自动配置,还需要配置 Spring Boot 的自动配置导入文件:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
那个配置文件有何作用呢,是否是必须的呢? 之后会为大家演示
这样可以避免每个微服务重复编写 Swagger 配置。
五、常用 Swagger 注解
@Tag
用于给接口分组:
java
@Tag(name = "管理员用户 API")
public class SysUserController {
}
它通常放在 Controller 类上,用于描述这一组接口的业务主题。
@Operation
用于描述具体操作:
java
@Operation(
summary = "新增管理员",
description = "根据提供的信息新增管理员用户"
)
常用属性包括:
summary:简短标题;description:详细说明。
@Parameter
用于描述单个参数:
java
@Parameter(
name = "userId",
in = ParameterIn.PATH,
description = "用户 ID"
)
参数位置可以是:
path:路径参数;query:查询参数;header:请求头;cookie:Cookie 参数。
@Parameters
用于一次描述多个参数:
java
@Parameters(value = {
@Parameter(
name = "userId",
in = ParameterIn.QUERY,
description = "用户 ID"
),
@Parameter(
name = "sex",
in = ParameterIn.QUERY,
description = "用户性别"
)
})
@ApiResponse
用于说明接口可能返回的结果:
java
@ApiResponse(
responseCode = "1000",
description = "操作成功"
)
@ApiResponse(
responseCode = "3101",
description = "用户已存在"
)
它能让接口文档中的响应说明更加清晰。
@Schema
用于描述数据模型及其属性,适合补充 DTO、VO 等对象的字段说明。
六、完整示例:管理员用户接口
java
@RestController
@RequestMapping("/sysUser")
@Tag(name = "管理员用户 API")
public class SysUserController {
@Operation(
summary = "新增管理员",
description = "根据提供的信息新增管理员用户"
)
@PostMapping("/add")
@ApiResponse(
responseCode = "1000",
description = "操作成功"
)
@ApiResponse(
responseCode = "2000",
description = "服务繁忙,请稍后重试"
)
@ApiResponse(
responseCode = "3101",
description = "用户已存在"
)
public R<Void> add(@RequestBody SysUserSaveDTO saveDTO) {
return null;
}
@DeleteMapping("/{userId}")
@Operation(
summary = "删除用户",
description = "通过用户 ID 删除用户"
)
@Parameters(value = {
@Parameter(
name = "userId",
in = ParameterIn.PATH,
description = "用户 ID"
)
})
@ApiResponse(
responseCode = "1000",
description = "成功删除用户"
)
@ApiResponse(
responseCode = "2000",
description = "服务繁忙,请稍后重试"
)
@ApiResponse(
responseCode = "3101",
description = "用户不存在"
)
public R<Void> delete(@PathVariable Long userId) {
return null;
}
@GetMapping("/detail")
@Operation(
summary = "用户详情",
description = "根据查询条件查询用户详情"
)
@Parameters(value = {
@Parameter(
name = "userId",
in = ParameterIn.QUERY,
description = "用户 ID"
),
@Parameter(
name = "sex",
in = ParameterIn.QUERY,
description = "用户性别"
)
})
@ApiResponse(
responseCode = "1000",
description = "成功获取用户信息"
)
@ApiResponse(
responseCode = "2000",
description = "服务繁忙,请稍后重试"
)
@ApiResponse(
responseCode = "3101",
description = "用户不存在"
)
public R<SysUserVO> detail(
Long userId,
@RequestParam(required = false) String sex) {
return null;
}
}
从这个例子可以看出,一个完整的接口描述至少应当回答四个问题:
- 这个接口属于哪一组功能?
- 这个接口具体做什么?
- 它需要哪些参数?
- 它可能返回哪些结果?
在使用swagger注解对我们的接口进行注释之后,接下来就是查看swagger配置是否生效
访问 http://localhost:9201/swagger-ui/index.html 可以进行接口文档的查看
这里的我的服务是使用9201端口启动的, 如果未来服务部署在远程服务器中而非本机, 这个路径也要按照具体情况来做出调整.

点开api中就可以看到具体的接口信息了,从接口地址,参数注释到状态码,都一览无余,是不是很舒服了
为什么需要Spring Boot 的自动配置导入文件
这里我们还能注意到 OpenAPI definition v0 OAS 3.0 和我们配置的openAi的配置信息不一致

这说明我们的配置SwaggerConfig没有生效, 这是为什么呢?
其实很简单,swagger位于公共模块中,但是这里我只启动了oj-system这一个服务,spring容器启动时只会扫描com.amadeus.system这个包路径下的所有Bean, swagger的Bean就不生效了
如何解决呢? 难道我们要违背swagger的公共组件特性,把他单独放到oj-system中吗?实则不然
我们只需要在resource包下创建出META-INF/spring目录
添加对应的配置文件即可
org.springframework.boot.autoconfigure.AutoConfiguration.imports

AutoConfiguration.imports 的作用是:
告诉 Spring Boot:当前公共模块中有哪些配置类需要自动加载。
这样就不需要在 OjSystemApplication 上添加:
java
@Import(SwaggerConfig.class)
效果:

何时使用swagger测试接口

swagger 还提供的测试接口的功能,点击Try it out 就可以自行测试, 这种测试和使用postman进行测试接口有什么区别呢? 我们该如何进行选择?
Swagger UI 更适合:
- 查看接口文档
- 查看请求参数、响应结构和数据模型
- 直接点击
Try it out调试接口 - 让前后端根据同一份 OpenAPI 文档协作
- 快速验证单个接口
Postman 更适合:
- 保存和管理大量接口
- 配置环境变量,例如开发、测试、生产环境
- 设置接口调用链,例如登录后自动提取 Token
- 编写断言和测试脚本
- 批量执行、接口回归测试
- 模拟复杂请求,例如文件上传、鉴权、多个请求组合
简单理解:
Swagger:以"看文档、试接口"为主
Postman:以"管理接口、自动化测试"为主
例如测试登录接口:
- Swagger 可以直接填写账号密码并发送请求
- Postman 可以保存登录接口,并自动提取返回的 Token,供后续新增、删除用户接口使用
Swagger 和 Postman 不是二选一。通常开发阶段用 Swagger 快速调试,接口联调和回归测试使用 Postman。
七、常见问题与设计建议
1. 不要只依赖 HTTP 状态码
HTTP 状态码无法完整表达业务结果。建议通过统一响应对象和业务状态码补充业务语义。
2. 状态码命名要统一
状态码枚举的命名、数字范围和描述信息应该保持一致。例如:
1000:成功;2000:服务器异常;3000:一般业务失败;3100:用户相关错误。
统一的编号规则有助于快速定位问题。
3. 不要把底层异常直接返回给前端
数据库异常、SQL 语句、堆栈信息等内容可能泄露系统细节。生产环境中应转换成统一的友好提示,并在服务端记录详细日志。
4. 接口注解要与代码同步
如果新增了接口却没有补充注解,Swagger 文档就可能缺少关键信息。建议在编写 Controller 的同时完成接口描述。
5. 参数位置必须写准确
路径参数和查询参数虽然都属于请求参数,但调用方式不同:
/sysUser/1
对应路径参数;
/sysUser/detail?userId=1
接口文档中如果标注错误,前端调用时就容易出现参数接收失败。
八、总结
接口文档的核心价值,是建立前后端都能理解的统一契约。
一套较完整的接口文档设计通常包括:
- 使用
Result<T>统一响应结构; - 使用 HTTP 状态码描述协议层结果;
- 使用业务状态码描述具体业务结果;
- 使用枚举集中管理状态码;
- 使用 Swagger 或 OpenAPI 自动生成文档;
- 使用注解描述接口、参数和响应;
- 使用公共模块复用 Swagger 配置。
如果没有统一接口规范,项目规模越大,前后端沟通成本和维护成本就越高。反过来,统一的响应格式、清晰的状态码体系和自动化接口文档,能够让接口从"能调用"变成"容易理解、容易维护、容易测试"。
下一步学习路线
- 先掌握 HTTP 方法和状态码;
- 理解 RESTful 接口设计;
- 熟悉 Spring MVC 参数绑定;
- 学习统一异常处理;
- 学习 Swagger/OpenAPI 的高级配置;
- 进一步了解 API 网关、接口鉴权和接口版本管理。