Spring Boot - 自动生成接口文档

前言

在目前主流的前后端分离项目中,桥接前后端的就是接口,因此,一份简洁易懂的接口文档就显得非常重要了。幸运的是,我们不必手动去编写这些接口文档,市面上已有许多成熟的第三方库可以自动为项目生成接口文档,在 Spring Boot 中,最常使用的接口文档自动生成库就是 Swagger。

本篇博文主要介绍在 Spring Boot 中集成 Swagger 3 的方法。

几个概念释义

  • OpenAPIOpenAPI规范(以前也被称为Swagger规范)制定了一份标准的对 REST API 接口的描述格式,具体的描述信息包含如下几方面:

    1. 可用终端接口(/user)及其各接口对应的操作(GET /user, POST /user
    2. 每个操作的输入、输出参数描述
    3. 认证方法
    4. 联系方式,许可证,使用条款等其他信息

    API规范 可以使用YAMLJSON格式进行描述,对于人和机器来说都是阅读友好的。

  • SwaggerSwagger是围绕OpenAPI规范构建的一系列开源工具,可以帮助我们设计、构建、记录和使用 REST API。主要的工具包含如下:

    1. Swagger Editor:可以在浏览器上编辑OpenAPI规范。
    2. Swagger UI:将OpenAPI规范渲染成可交互 API 接口文档。
    3. Swagger Codegen:依据OpenAPI规范文件生成服务器存根和客户端库。

更多详细信息,请参考官网:Specification

简单来说,OpenAPI就是定义描述 REST API 的规范,而Swagger就是对OpenAPI规范的实现。

基本使用

在 Spring Boot 中集成 Swagger 3,步骤如下:

  1. pom.xml中导入依赖

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    
  2. 依赖导入完成后,直接在浏览器输入以下网址:

    http://localhost:8080/swagger-ui/
    

    此时就可以直接看到项目接口文档了。

  3. (可选)Swagger 3 提供了一些注解,可以让我们自定义接口描述信息。相关的注解如下表所示:

    :Swagger 3 提供的注解与 Swagger2 名称不同,下表列举了其对应关系:

    Swagger 2 Swagger 3 (OpenAPI 3) 注解位置
    @Api @Tag(name = "接口类描述") Controller类上
    @ApiOperation @Operation(summary = "接口方法描述") Controller方法上
    @ApiImplicitParams @Parameters Controller方法上
    @ApiImplicitParam @Parameter(description=“参数描述”) @Paramseters内部
    @ApiParam @Parameter(description=“参数描述”) Controller方法参数上
    @ApiIgnore @Parameter(hidden = true)@Operation(hidden = true)@Hidden -
    @ApiModel @Schema DTO 类上
    @ApiModelProperty @Schema DTO 属性上

    一个简单的示例如下所示:

    @RestController
    @RequestMapping("/user")
    @Tag(name = "用户接口")
    public class UserApi {
    
        @PostMapping
        @Operation(summary = "添加用户")
        public String addUser(@RequestBody User user) {
            //...
        }
    
        @GetMapping("/all")
        @Operation(description = "获取所有用户")
        public List<User> getAllUsers() {
            //...
        }
    }
    

可以看到,Swagger 3 的使用还是非常简单的。

自定义配置

如果需要更细致的自定义配置,我们可以自定义一个配置类,然后注入一个Docket数据实例,自定义配置 Swagger。

以下列举几种常见的配置示例:

  1. 配置文档相关信息:

    @Configuration
    @ConditionalOnProperty(value = "springfox.documentation.enabled", havingValue = "true", matchIfMissing = true)
    public class SwaggerConfig {
        @Bean
        public Docket docket(){
            return new Docket(DocumentationType.OAS_30)
                    // 配置接口相关信息
                    .apiInfo(apiInfo())
                    // 选择哪些接口作为 swagger 的 doc 发布
                    .select()
                    .build();
        }
    
        private ApiInfo apiInfo(){
            return new ApiInfoBuilder()
                    .title("XX项目接口文档")
                    .description("XX项目描述")
                    .contact(new Contact("作者", "作者URL", "作者Email"))
                    .version("1.0")
                    .build();
        }
    }
    
  2. 配置包扫描路径:

    @Bean
    public Docket docket(){
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .select()
                //apis: 添加swagger接口提取范围
                // 指定扫描包
                .apis(RequestHandlerSelectors.basePackage("com.yn.controller"))
                // 指定扫描方法注解
                // .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                // 所有路径
                .paths(PathSelectors.any())
                .build();
    }
    
  3. Api 分组:默认情况下,Swagger 将扫描到的接口都归属于default组,我们可以通过对每个接口路径(/public/admin)各自配置一个Docket,显式设置其分组:

    @Bean
    public Docket publicApi() {
        return new Docket(DocumentationType.OAS_30)
                .groupName("api-public")
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.yn.controller.open"))
                // localhost:8080/public
                .paths(PathSelectors.regex("/public.*"))
                .build();
    }
    
    @Bean
    public Docket privateApi() {
        return new Docket(DocumentationType.OAS_30)
                .groupName("api-admin")
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.yn.controller.admin"))
                .paths(PathSelectors.regex("/admin.*"))
                .build();
    }
    
  4. 请求头添加 token:越来越多的项目采用前后端分离架构,此时经常使用 token 来作为鉴权机制,因此前端每次请求都必须携带上 token,我们可以配置下 Swagger,让其每次请求自动携带我们设置的 token,只需对相应Docket进行配置即可,如下所示:

    @Bean
    public Docket addJwtToken() {
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                .securitySchemes(Collections.singletonList(HttpAuthenticationScheme.JWT_BEARER_BUILDER
                         // 显示用
                        .name("JWT")
                        .build()))
                .securityContexts(Collections.singletonList(SecurityContext.builder()
                        .securityReferences(Collections.singletonList(SecurityReference.builder()
                                .scopes(new AuthorizationScope[0])
                                .reference("JWT")
                                .build()))
                        // 声明作用域
                        .operationSelector(o -> o.requestMappingPattern().matches("/.*"))
                        .build()))
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build();
    }
    

    配置完成后,打开该Docker配置对应的页面,就可以看到Authorize的按钮,如下图所示:

    Authorize

    点击该按钮,手动输入 token,最后点击Authorize按钮确认即可,如下图所示:

    Enter Token

Swagger 更多配置选项,请参考官方文档:springfox

其他注意事项

Swagger 在使用过程中,有一些事项可以注意一下,避免出现问题。比如:

  • 区分环境:通常情况下,Swagger 只在开发环境或测试环境下开启,在生产环境下必须进行关闭。可以通过在配置文件application.properties中配置使能或失能 Swagger:

    # 生产环境失能 Swagger
    springfox.documentation.enabled=false
    # 或者
    springfox.documentation.swagger-ui.enabled=false
    

    :在springfox-boot-starter.jar包中,我们可以找到/META-INF/spring.factories配置文件,其内容如下:

    # Auto Configure
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    springfox.boot.starter.autoconfigure.OpenApiAutoConfiguration
    

    可以看到,Swagger 的自动配置文件类为OpenApiAutoConfiguration,其源码如下所示:

    @Configuration
    @EnableConfigurationProperties({SpringfoxConfigurationProperties.class})
    @ConditionalOnProperty(
        value = {"springfox.documentation.enabled"},
        havingValue = "true",
        matchIfMissing = true
    )
    @Import({OpenApiDocumentationConfiguration.class, SpringDataRestConfiguration.class, BeanValidatorPluginsConfiguration.class, Swagger2DocumentationConfiguration.class, SwaggerUiWebFluxConfiguration.class, SwaggerUiWebMvcConfiguration.class})
    @AutoConfigureAfter({WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class})
    public class OpenApiAutoConfiguration {
        public OpenApiAutoConfiguration() {
        }
    }
    

    这里我们主要关注@ConditionalOnProperty,可以看到,当springfox.documentation.enabled设置为true(缺省默认也为true)时,才会加载OpenApiAutoConfiguration自动配置类。因此,这里也是为什么我们前面自定义配置类SwaggerConfig也带上这个配置,这样可以确保在springfox.documentation.enabled=false时,我们的自定义配置类SwaggerConfig也不会被加载。

    :Spring Boot 多环境配置方法可参考文章:Spring Boot - 多环境配置

  • 安全框架放行:如果项目中使用了 Spring Security 这种权限认证框架,应当注意要把 Swagger 添加到白名单中:

    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            String[] SWAGGER_WHITELIST = {
                    "/swagger-ui.html",
                    "/swagger-ui/*",
                    "/swagger-resources/**",
                    "/v2/api-docs",
                    "/v3/api-docs",
                    "/webjars/**"
            };
            web.ignoring().antMatchers(SWAGGER_WHITELIST);
        }
    }
    
  • 统一数据下发异常:如果项目中使用了@RestControllerAdvice拦截所有接口请求,那么 Swagger 的内置接口也同样会被拦截修改,导致Unable to infer base url错误,此时,只需为@RestControllerAdvice设置拦截指定包目录即可,如下所示:

    // 只拦截 com.yn.controller 包
    @RestControllerAdvice(basePackages = {"com.yn.controller"})
    public class FormatResponseBodyAdvice implements ResponseBodyAdvice<Object> {...}
    
  • @EnableWebMvc异常:当项目中使用了@EnableWebMvc注解后,会导致 Swagger 无法访问。原因是在 Spring Boot 中,注解在配置类(即@Configuration)类上的@EnableWebMvc会完全掌控 SpringMVC 配置,导致 Spring Boot 对 SpringMVC 的自动配置失效(即不加载自动配置),从而间接影响到了 Swagger。解决该问题的方法有很多,这里列举几种:

    1. 移除@EnableWebMvc:对于 Spring Boot 项目,通常不建议在配置类(@Configuration)上直接使用@EnableWebMvc,很多对 SpringMVC 的配置,可以直接搜索 Spring Boot 对应的配置选项即可,大多数情况下无需直接配置原生 SpringMVC。
    2. 添加视图解析器ViewResolver:使用@EnableWebMvc后,SpringMVC 中,内置的视图解析器无法对 Swagger 的视图进行解析,因此手动为DispatcherServlet添加 Spring 内置的视图解析器即可:
    @Configuration
    @EnableWebMvc
    public class WebConfiguration implements WebMvcConfigurer {
    
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.viewResolver(new InternalResourceViewResolver());
        }
    }
    

    :可单步调式DispatcherServlet,在DispatcherServlet#resolveViewName方法内就是对视图进行解析的过程,此处可查看到DispatcherServlet具体所使用到的ViewResolver

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容