在实际的后端开发场景中,一个API接口可能会被多个不同的业务模块调用,比如用户查询接口既会被用户管理模块使用,也会被权限管理模块使用。如果Swagger文档中同一个接口在所有标签下展示的描述完全相同,会让不同业务的使用者难以快速理解该接口在当前模块下的具体作用。因此为同一API接口按标签提供差异化描述是优化API文档的重要手段。

核心实现思路
Swagger基于OpenAPI规范生成文档,其核心是通过注解或者配置文件定义接口、参数、响应等信息。要实现同一接口按标签差异化描述,核心思路是为同一个接口方法关联多个不同的标签,并且为每个标签单独配置对应的接口描述信息,而不是只配置一份全局的接口描述。
基于Swagger 2(Springfox)的实现方式
Springfox是Spring生态中常用的Swagger 2实现,我们可以通过自定义Docket配置和接口注解来实现差异化描述。
步骤1:接口方法关联多个标签
首先在Controller的接口方法上使用@ApiOperation注解,通过tags属性指定多个标签,不过默认情况下@ApiOperation的notes属性是全局生效的,需要结合额外配置实现差异化。
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
// 关联两个标签:用户管理、权限管理
@ApiOperation(value = "查询用户详情", tags = {"用户管理", "权限管理"})
@GetMapping("/detail")
public String getUserDetail() {
return "用户详情数据";
}
}
步骤2:自定义OperationBuilderPlugin实现差异化描述
通过实现OperationBuilderPlugin接口,在文档构建阶段根据当前接口的标签动态修改接口的描述信息。
import com.google.common.collect.Lists;
import io.swagger.models.Operation;
import org.springframework.stereotype.Component;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.OperationBuilderPlugin;
import springfox.documentation.spi.service.contexts.OperationContext;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Component
public class TagDescriptionPlugin implements OperationBuilderPlugin {
// 存储标签和对应描述的映射关系
private static final Map<String, String> TAG_DESC_MAP = new ConcurrentHashMap<>();
static {
TAG_DESC_MAP.put("用户管理", "该接口用于用户管理模块查询用户的基础信息,包含用户名、手机号等字段");
TAG_DESC_MAP.put("权限管理", "该接口用于权限管理模块查询用户的角色和权限信息,用于权限校验逻辑");
}
@Override
public void apply(OperationContext context) {
Operation operation = context.operation();
List<String> tags = operation.getTags();
if (tags == null || tags.isEmpty()) {
return;
}
// 取第一个标签作为当前展示的标签,也可以根据需求调整逻辑
String currentTag = tags.get(0);
String desc = TAG_DESC_MAP.get(currentTag);
if (desc != null) {
operation.setDescription(desc);
}
}
@Override
public boolean supports(DocumentationType delimiter) {
return delimiter.equals(DocumentationType.SWAGGER_2);
}
}
基于Swagger 3(springdoc-openapi)的实现方式
Swagger 3对应的Spring生态实现是springdoc-openapi,其配置方式更加灵活,支持直接通过注解或者全局配置实现差异化描述。
步骤1:使用@Tag注解关联标签
在接口方法上使用@Tag注解指定多个标签,同时可以结合@Operation注解的description属性,通过自定义配置类动态替换描述。
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/user")
public class UserController {
@Tag(name = "用户管理")
@Tag(name = "权限管理")
@Operation(summary = "查询用户详情")
@GetMapping("/detail")
public String getUserDetail() {
return "用户详情数据";
}
}
步骤2:自定义OpenApiCustomiser修改描述
通过实现OpenApiCustomiser接口,遍历所有路径下的操作,根据操作所属的标签动态设置不同的描述。
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.tags.Tag;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class SwaggerConfig {
private static final Map<String, String> TAG_DESC_MAP = new ConcurrentHashMap<>();
static {
TAG_DESC_MAP.put("用户管理", "该接口用于用户管理模块查询用户的基础信息,包含用户名、手机号等字段");
TAG_DESC_MAP.put("权限管理", "该接口用于权限管理模块查询用户的角色和权限信息,用于权限校验逻辑");
}
@Bean
public OpenApiCustomiser tagDescriptionCustomiser() {
return openApi -> {
Map<String, PathItem> paths = openApi.getPaths();
if (paths == null) {
return;
}
paths.forEach((path, pathItem) -> {
// 处理GET请求对应的操作
Operation getOp = pathItem.getGet();
if (getOp != null) {
updateOperationDesc(getOp);
}
// 可以扩展处理POST、PUT等其他请求类型
});
};
}
private void updateOperationDesc(Operation operation) {
List<Tag> tags = operation.getTags();
if (tags == null || tags.isEmpty()) {
return;
}
// 取第一个标签的描述
String tagName = tags.get(0).getName();
String desc = TAG_DESC_MAP.get(tagName);
if (desc != null) {
operation.setDescription(desc);
}
}
}
注意事项
- 差异化描述的标签映射关系建议统一管理,避免散落在代码各处,方便后续维护。
- 如果同一个接口关联的多个标签都需要展示对应的描述,可以调整逻辑将多个标签的描述拼接展示,或者根据前端传递的标签参数动态返回对应描述。
- Swagger版本不同配置方式差异较大,需要根据项目实际使用的Swagger版本选择对应的实现方案。
- 配置完成后需要重启项目,访问Swagger UI页面验证不同标签下的接口描述是否符合预期。
需要注意的是,OpenAPI规范本身并不直接支持同一个操作在不同标签下有不同的描述,上述方案都是通过Swagger的扩展机制在文档构建阶段动态修改实现的,不同Swagger版本的扩展点可能存在差异。