在API驱动的开发时代,维护实时、准确的接口文档仍是团队协作的核心痛点。Swagger通过自动化扫描与注解解析,将文档与代码深度绑定,彻底终结“文档滞后于代码”的困境。而掌握分组配置、安全策略及UI定制等高阶技巧,更能让文档引擎成为API治理的枢纽——从精准的环境隔离,到动态的Mock数据生成,直至无缝嵌入CI/CD流水线。本指南将深入解析Swagger的进阶实践,助你释放自动化文档的完整潜能

多分组配置

微服务模块化开发时,需按业务模块分离API文档。

package com.morris.swagger.demo.complex.group;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@Configuration
@EnableOpenApi //Enable open api 3.0.3 spec
public class SwaggerConfig {
    @Bean
    public Docket userApi() {
        return new Docket(DocumentationType.OAS_30)
                .groupName("用户模块")
                .select()
                .paths(PathSelectors.ant("/user/**"))
                // 也可以指定某个包
//                .apis(RequestHandlerSelectors.basePackage("com.example.user"))
                .build();
    }

    @Bean
    public Docket orderApi() {
        return new Docket(DocumentationType.OAS_30)
                .groupName("订单模块")
                .select()
                .paths(PathSelectors.ant("/order/**"))
                .build();
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

可以根据具体的包名或者路径进行分割。

效果:访问swagger-ui.html时通过顶部下拉菜单切换模块。

【swagger】Swagger高级使用_springdoc

隐藏敏感参数

例如屏蔽实体类中的密码字段。

public class User {
   @ApiModelProperty(hidden = true) // 文档中隐藏
   private String password;
}
  • 1.
  • 2.
  • 3.
  • 4.

手动指定参数与响应

@ApiImplicitParam和@ApiImplicitParams用于精准描述请求参数

适用场景:非实体类参数(如@RequestParam、@PathVariable、@RequestHeader等),或无法通过实体类字段自动生成文档的参数。

@ApiResponse和@ApiResponses用于定义接口返回值规范。

适用场景:明确接口的响应状态码、业务含义及返回数据结构,尤其适用于异常状态(如400、500)

@GetMapping("/get")
@ApiOperation("查询用户")
@ApiImplicitParam(value = "Token", name = "token", type = "string", paramType = "header")
@ApiResponse(code = 200, message = "成功", response = R.class)
public R<User> getById(Long userId) {
    return R.ok(new User(userId, "morris", 18));
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

@ApiImplicitParam的局限性

  • 不替代校验注解,仅生成文档,不影响实际请求校验(如@RequestParam(required=true)仍需独立配置)
  • 泛型支持弱:dataType不支持泛型(如List),需简写为List。

@ApiResponse的响应一致性:若方法返回String但response声明为User.class,文档与代码将不一致,导致误导

Knife4j替代原生UI

Knife4j界面更美观,支持离线文档导出。

集成只需要引入下面的依赖:

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-spring-ui</artifactId>
    <version>3.0.3</version>
</dependency>
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

访问路径:http://localhost:8080/doc.html

【swagger】Swagger高级使用_openapi_02

每个接口header中增加token描述

现在的项目开发基本上都是前后端分离,很多API的调用都需要经过token的验证,本文就介绍怎么在swagger的header中自动添加token,以及各种实现方式的优缺点。

在每个接口上手动添加header

这种方式就是在每个接口上手动添加header。

package com.morris.swagger.web;

import com.morris.swagger.vo.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
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("header")
@Api(tags = "为单个接口添加header")
public class HeaderController {
    @GetMapping("token")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "header", name = "token", required = true),
    })
    @ApiOperation("token")
    public R<Void> header() {
        return R.ok();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

效果如下:

【swagger】Swagger高级使用_swagger_03

这种方法的缺点就是需要手动在每个方法上添加@ApiImplicitParam注解来指定header参数,那么有没有一种方式一次性在所有的方法上面加上header参数呢?

全局对每个API都添加header字段

重点是在原先swagger的Docket对象后面添加globalOperationParameters方法。

package com.morris.swagger.demo.token.global;

import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.schema.ScalarType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ParameterType;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

import static java.util.Collections.singletonList;

@Configuration
@EnableOpenApi //Enable open api 3.0.3 spec
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {

        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.regex("(?!/error.*).*"))
                .build()
                .globalRequestParameters(
                        singletonList(new springfox.documentation.builders.RequestParameterBuilder()
                                // 不能叫Authorization
                                .name("token")
                                .description("token")
                                .in(ParameterType.HEADER)
                                .required(true)
                                .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                                .build()));
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Swagger API")
                .description("this is a description")
                .termsOfServiceUrl("http://springfox.io")
                .contact(new Contact("springfox", "https://morris131.github.io", "morris131@163.com"))
                .license("Apache License Version 2.0")
                .licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
                .version("3.0")
                .build();
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.

运行的效果如图:

【swagger】Swagger高级使用_springfox_04

全站统一header设置

这种方案就简化了第一种方案,在swagger UI上出现一个Authorize按钮,一次输入header参数,全站使用。

package com.morris.swagger.demo.token.site;

import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.Arrays;
import java.util.List;

import static java.util.Collections.singletonList;

@Configuration
@EnableOpenApi //Enable open api 3.0.3 spec
public class SwaggerConfig {
    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.regex("(?!/error.*).*"))
                .build()
                .securityContexts(Arrays.asList(securityContext()))
                // ApiKey的name需与SecurityReference的reference保持一致
                .securitySchemes(Arrays.asList(new ApiKey("token", "token", SecurityScheme.In.HEADER.name())));
    }

    private SecurityContext securityContext() {
        return SecurityContext.builder()
                .securityReferences(defaultAuth())
                //.forPaths(PathSelectors.regex("/*.*"))
                .build();
    }

    private List<SecurityReference> defaultAuth() {
        AuthorizationScope authorizationScope
                = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return singletonList(
                new SecurityReference("token", authorizationScopes));
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Swagger API")
                .description("this is a description")
                .termsOfServiceUrl("http://springfox.io")
                .contact(new Contact("springfox", "https://morris131.github.io", "morris131@163.com"))
                .license("Apache License Version 2.0")
                .licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
                .version("3.0")
                .build();
    }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.

全局设置完这个参数后,每个方法都会带上header参数。

【swagger】Swagger高级使用_spring_05

这种方式好像不传参数也能访问接口,没法做到必填校验。