• SpringBoot学习小结之Swagger


    swagger

    前言

    Swagger是什么?

    Swagger API 项目最初由 Wordnik 的技术联合创始人 Tony Tam 于 2011 年创建,主要针对在 Wordnik 的产品开发过程中 API 文档自动化客户端 SDK 生成的需求。设计师/开发人员 Zeke Sikelianos 创建了 Swagger 这个名字,Swagger API 项目于 2011 年 9 月开源。

    2015 年 11 月,维护 Swagger 的公司 SmartBear Software 宣布在 Linux 基金会的赞助下,创建了一个名为 OpenAPI Initiative 的新组织,包括谷歌、IBM 和微软在内的各种公司都是创始成员。

    2016 年 1 月 1 日,Swagger 规范更名为 OpenAPI 规范,并移至 GitHub 上。

    Swagger 官网上将 Swagger 分为三大块:

    1. OpenAPI 规范
    2. Swagger 开源工具
    3. SwaggerHub

    其中,Swagger 开源工具可以分成以下三部分:

    1. Swagger Editor: 编辑器,可以在浏览器中以 YAML 格式编辑 OpenAPI API 定义并实时预览文档。
    2. Swagger Codegen:代码生成器,可以基于根据 OpenAPI (以前称为 Swagger ) 定义的 RESTful API 可以自动生成服务端和客户端代码.。
    3. Swagger UI:UI部分,根据 OpenAPI(以前称为Swagger )规范自动生成的 HTML 界面,可视化 API 资源,能够与之交互。

    SpringBoot中使用Swagger

    在 SpringBoot 项目中一般使用 Swagger 用的是 Swagger UI 部分,用于接口的文档在线自动生成和功能测试。 常见的库是使用 springfox 提供的 springfox,不过最新更新在2020年,已经两年没更新,最新的替代品是 springdoc-openapi

    下面会介绍 springfox swagger 的使用,虽然已经不再维护,但文档齐全,使用的人多,有什么问题可以十分方便找到答案。

    一、pom依赖

    不同 swagger 版本选择pom依赖也不同

    swagger2

    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger2artifactId>
        <version>2.9.2version>
    dependency>
    
    <dependency>
    	<groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger-uiartifactId>
        <version>2.9.2version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    swagger3(openapi3) 提供了boot-starter版本,但自此之后再无更新

    <dependency>
    	<groupId>io.springfoxgroupId>
    	<artifactId>springfox-boot-starterartifactId>
    	<version>3.0.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    swagger2 ui 访问路径:http://localhost:8080/swagger-ui.html

    swagger3 ui 访问路径:http://localhost:8080/swagger-ui/index.html

    下面会使用 swagger3 为例,探索 swagger 使用

    二、常用注解

    swagger 以下注解都位于 io.swagger.annotations 包内

    注解名标记范围含义
    @ApiTYPE用于标注一个 Controller,表明是 swagger 资源 。在默认情况下,Swagger-Core 只会扫描解析具有 @Api 注解的类
    @ApiImplicitParamMETHOD用于标注 @ApiOperation 操作中的单个参数
    @ApiImplicitParamsMETHOD用于标注 @ApiOperation 操作中的多个参数
    @ApiModelTYPE用于标注一个类,表明此类是 swagger 的 Model 类
    @ApiModelPropertyMETHOD,FIELD用于描述被 @ApiModel 标记类的属性
    @ApiOperationMETHOD,FIELD表明是一个 http 请求的操作
    @ApiParamPARAMETER,METHOD,FIELD和 @ApiImplicitParam 类似, 但只能和 JAX-RS 1.x/2.x 注解结合使用
    @ApiResponseMETHOD用于描述 @ApiOperation 单个响应
    @ApiResponsesMETHOD用于描述 @ApiOperation 多个响应
    @AuthorizationMETHOD定义要在资源或操作上使用的授权方案。应该在 @Api 或 @ApiOperation 中使用
    @AuthorizationScopeMETHOD描述 OAuth2 授权范围,应该在 @Authorization 中使用
    @ExampleANNOTATION_TYPE用于描述一个示例
    @ExamplePropertyANNOTATION_TYPE用于描述一个示例属性
    @ResponseHeaderMETHOD定义 @ApiResponse head部分

    下面注解不在 io.swagger.annotations 包下面

    注解名标记范围含义
    @EnableSwaggerTYPE启用 swagger 1.2 规范
    @EnableSwagger2TYPE启用 swagger 2.0 规范
    @EnableOpenApiTYPE启用 open api 3.0.3 规范
    @ApiIgnoreMETHOD, TYPE, PARAMETER忽略所标记的方法、类或参数,不会在UI界面显示

    三、配置

    一个 Swagger3 文档例子 UI 如下

    swagger_index_html

    可以通过 java 代码对界面进行配置

    @Configuration
    public class SwaggerConfig {
        @Bean
        public Docket createRestApi(Environment environment) {
           	//设置要显示的swagger的环境
            Profiles profiles = Profiles.of("dev", "test");
    
            //通过environment.acceptsProfiles判断是否处在自己设定的环境中
            boolean flag = environment.acceptsProfiles(profiles);
            
            return new Docket(DocumentationType.OAS_30)
                	.enable(flag)
                    .apiInfo(apiInfo())
                    .directModelSubstitute(LocalTime.class, String.class)
                    .directModelSubstitute(LocalDate.class, String.class)
                    .directModelSubstitute(LocalDateTime.class, String.class)
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.aabond.demoswagger.controller"))
                    .paths(PathSelectors.any()) // 可以通过PathSelectors.ant("/a/**")来分隔 进行分组
                    .build();
        }
    
        //生成接口信息,包括标题、联系人、版本等
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Swagger3 demo接口文档")
                    .description("如有疑问,请联系开发工程师。")
                    .contact(new Contact("aabond", "http://localhost:8080/swagger-ui/index.html", "aabond@foxmail.com"))
                    .version("1.0")
                    .build();
        }
    
        @Bean
        public UiConfiguration uiConfig() {
            return UiConfigurationBuilder.builder()
                    .docExpansion(DocExpansion.NONE) // 接口文档展开
                    .operationsSorter(OperationsSorter.ALPHA) // http操作排序
                    .defaultModelRendering(ModelRendering.EXAMPLE) // Model渲染
                    .supportedSubmitMethods(DEFAULT_SUBMIT_METHODS) // 支持的http方法
                    .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

    Docket 是 主要的配置类,如果包含分组可以配置多个,上面使用 directModelSubstitute 是为了在前台用 String 接收时间类型

    UiConfiguration 中可以配置一些UI界面选项

    docExpansion 接口文档展开

    • none 不展开
    • list 展开
    • full 全部展开,包括接口的详细信息

    operationsSorter http操作排序

    • alpha 字母排序
    • method 根据 http 方法排序 (http方法根据字母顺序排序)

    defaultModelRendering Model渲染

    • EXAMPLE 接口 response 默认显示 example

      swagger_reponse_example

    • MODEL 接口 response 默认显示 schema

      swagger_response_schema

    四、示例

    下面以一个选课系统来演示 swagger 使用

    4.1 代码

    课程类

    @ApiModel
    @Data
    public class Course {
        @ApiModelProperty(value = "课程ID")
        private Long id;
    
        @ApiModelProperty(value = "名字")
        private String name;
    
        @ApiModelProperty(value = "类别")
        private String category;
    
        @ApiModelProperty(value = "学分")
        private String credit;
    
        @ApiModelProperty(value = "学生数量")
        private Integer number;
    
        @ApiModelProperty(value = "老师名字")
        private List<String> teachers;
    
        @ApiModelProperty(value = "上课时间-星期")
        private DayOfWeek timeWeek;
    
        @ApiModelProperty(value = "开始时间")
        @DateTimeFormat(pattern = "HH:mm:ss")
        private LocalTime startTime;
    
        @ApiModelProperty(value = "结束时间")
        @DateTimeFormat(pattern = "HH:mm:ss")
        private LocalTime endTime;
    
        @ApiModelProperty(value = "地点")
        private String place;
    }
    
    • 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

    学生类

    @ApiModel
    @Data
    public class Student {
    
        @ApiModelProperty(value = "学号")
        private String id;
    
        @ApiModelProperty(value = "姓名")
        private String name;
    
        @ApiModelProperty(value = "密码")
        private String password;
    
        @ApiModelProperty(value = "专业")
        private String major;
    
        @ApiModelProperty(value = "年级")
        private CollegeGradeEnum grade;
    
        @ApiModelProperty(value = "性别")
        private String gender;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    学生年级枚举

    public enum CollegeGradeEnum {
        FRESHMAN, 
        SOPHOMOREER, 
        JUNIOR, 
        SENIOR 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    公共VO

    @ApiModel
    @Data
    @Builder
    public class CommonVo<T> {
    
        @ApiModelProperty("状态码")
        private String code;
    
        @ApiModelProperty("状态信息")
        private String message;
    
        @ApiModelProperty("数据")
        private T data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    课程Controller

    @Api(value = "courseAPI",tags = "课程API")
    @RestController
    @RequestMapping
    public class CourseController {
    
        private static final Logger logger = LoggerFactory.getLogger(CourseController.class);
    
        @ApiOperation("创建一门课程")
        @ApiImplicitParam(name = "course", value = "课程信息", dataTypeClass = Course.class, paramType = "body")
        @RequestMapping(value = "/createCourse",method = RequestMethod.POST)
        public ResponseEntity<CommonVo> createCourse(@RequestBody Course course) {
            return ResponseEntity.ok(CommonVo.builder().code("0").message("success").build());
        }
    
    
        @ApiOperation("修改课程信息")
        @ApiImplicitParams({
                @ApiImplicitParam( name = "id", value = "课程id", paramType = "query", dataTypeClass = Long.class, required = true),
                @ApiImplicitParam( name = "category", value = "课程类别", paramType = "query", dataTypeClass = String.class),
                @ApiImplicitParam( name = "name", value = "课程名称", paramType = "query", dataTypeClass = String.class),
                @ApiImplicitParam( name = "timeWeek", value = "星期", paramType = "query", dataTypeClass = String.class),
                @ApiImplicitParam( name = "startTime", value = "上课开始时间", paramType = "query", dataTypeClass = String.class),
                @ApiImplicitParam( name = "endTime", value = "上课结束时间", paramType = "query", dataTypeClass = String.class),
                @ApiImplicitParam( name = "credit", value = "学分", paramType = "query", dataTypeClass = String.class),
                @ApiImplicitParam( name = "teachers", value = "老师", paramType = "query", dataTypeClass = String.class, allowMultiple = true),
                @ApiImplicitParam( name = "number", value = "上课人数", paramType = "query", dataTypeClass = Integer.class),
                @ApiImplicitParam( name = "place", value = "上课地点", paramType = "query", dataTypeClass = String.class),
        })
        @RequestMapping(value = "/updateCourse",method = RequestMethod.PUT)
        public ResponseEntity<CommonVo<Course>> updateCourse(Course course){
            logger.info("{}}", course);
            return ResponseEntity.ok(CommonVo.<Course>builder().code("200").data(course).build());
        }
        
        @ApiOperation("操作课程")
        @RequestMapping(value = "/testCourse", method = RequestMethod.POST)
        public ResponseEntity<CommonVo<Course>> test(@ApiParam(name = "course") @RequestBody Course course) {
            return null;
        }
    
    
    
        @ApiOperation("查看课程信息")
        @RequestMapping(value = "/courseList",method = RequestMethod.GET)
        public ResponseEntity<CommonVo<List<Course>>> courseList(@ApiParam(name = "studentId", value = "学号") String studentId) {
            return null;
        }
    
        @ApiOperation("查看单个课程信息")
        @RequestMapping(value = "/course",method = RequestMethod.GET)
        public ResponseEntity<CommonVo<Course>> course(@ApiParam(name = "courseId", value = "课程Id") Long courseId) {
            return null;
        }
    
        @ApiOperation("删除课程")
        @RequestMapping(value = "/deleteCourse",method = RequestMethod.DELETE)
        public ResponseEntity<CommonVo> deleteCourse(Long courseId) {
            return null;
        }
    }
    
    • 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

    学生Controller

    @Api(value = "student API", tags = "学生API")
    @RestController
    @RequestMapping("/home")
    public class StudentController {
    
        @ApiOperation("新增学生")
        @ApiImplicitParam(name = "student", value = "学生信息", dataTypeClass = Student.class, paramType = "body")
        @RequestMapping(value = "/createCourse",method = RequestMethod.POST)
        public Object createCourse(@RequestBody Student student) {
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.2 后台接收前端字段

    一般通过 @ApiImplicitParam 或 @ApiParam 来定义接收的字段,@ApiImplicitParam 用处比较广,可以接收表单和 json 等各种字段,常用属性如下所示

    1. name

      参数名字

    2. value

      参数描述

    3. paramType

      参数类型,可以为以下部分

      • header:@RequestHeader

      • query:@RequestParam

      • path:@PathVariable

      • body:@RequestBody

      • form:表单

    4. dataTypeClass

      参数类型,String类型用的比较多,默认 Void.class

    5. required

      参数是否必填,默认 false

    6. allowMultiple

      是否数组,,默认 false

    @ApiImplicitParam

    swagger_no_json_query.png

    @ApiParam 在官方文档中注明需要和 JAX-RS 1.x/2.x 注解结合使用,实际使用发现可以和 SpringBoot 中注解一起使用,使用效果和 @ApiImplicitParam 类似

    swagger_api_param_example

    字段含义可以通过Scchema 查看

    4.3 后台返回前端字段

    后台返回前端字段,不需要特别指出,只需要使用 @ApiModel @ApiModelProperty将返回数据类定义好,swagger 会自动将 controller 返回值解析为 json 数据.。以上面示例为例,ResponseEntity>会自动解析为下面字段

    swagger_response_common_data

    每个字段的含义,可以通过schema标签查看

    swagger_response_common_scheme

    五、拓展使用

    5.1 隐藏前端的 schema

    在UiConfiguration uiConfig配置defaultModelsExpandDepth(-1)

    5.2 分组

    当Controller很多时,页面查找不方便,可以根据路径将各个Controller进行分组

    @Bean
    public Docket studentApi(Environment environment) {
        //设置要显示的swagger的环境
        Profiles profiles = Profiles.of("dev", "test");
    
        //通过environment.acceptsProfiles判断是否处在自己设定的环境中
        boolean flag = environment.acceptsProfiles(profiles);   
    
        return new Docket(DocumentationType.OAS_30)
            	.enable(flag)
                .apiInfo(apiInfo())
                .groupName("学生")
                .directModelSubstitute(LocalTime.class, String.class)
                .directModelSubstitute(LocalDate.class, String.class)
                .directModelSubstitute(LocalDateTime.class, String.class)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.aabond.demoswagger.controller"))
                .paths(PathSelectors.ant("/student/**")) // 可以通过PathSelectors.ant("/zhao/**")来分隔 进行分组
                .build();
    }
    
    @Bean
    public Docket courseApi(Environment environment) {
        //设置要显示的swagger的环境
        Profiles profiles = Profiles.of("dev", "test");
    
        //通过environment.acceptsProfiles判断是否处在自己设定的环境中
        boolean flag = environment.acceptsProfiles(profiles); 
        
        return new Docket(DocumentationType.OAS_30)
                .enable(flag)
                .apiInfo(apiInfo())
                .groupName("课程")
                .directModelSubstitute(LocalTime.class, String.class)
                .directModelSubstitute(LocalDate.class, String.class)
                .directModelSubstitute(LocalDateTime.class, String.class)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.aabond.demoswagger.controller"))
                .paths(PathSelectors.ant("/course/**")) // 可以通过PathSelectors.ant("/zhao/**")来分隔 进行分组
                .build();
    }
    
    @Bean
    public Docket defaultApi(Environment environment) {
        //设置要显示的swagger的环境
        Profiles profiles = Profiles.of("dev", "test");
    
        //通过environment.acceptsProfiles判断是否处在自己设定的环境中
        boolean flag = environment.acceptsProfiles(profiles); 
        
        return new Docket(DocumentationType.OAS_30)
                .enable(flag)
                .apiInfo(apiInfo())
                .groupName("default")
                .directModelSubstitute(LocalTime.class, String.class)
                .directModelSubstitute(LocalDate.class, String.class)
                .directModelSubstitute(LocalDateTime.class, String.class)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.aabond.demoswagger.controller"))
                .paths(PathSelectors.any()) 
                .build();
    }
    
    //生成接口信息,包括标题、联系人等
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("Swagger3 demo接口文档")
                .description("如有疑问,请联系开发工程师。")
                .contact(new Contact("aabond", "http://localhost:8080/swagger-ui/index.html", "aabond@foxmail.com"))
                .version("1.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
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    5.3 生成离线文档

    swagger 通过 swagger2markup 这个项目将文档转成静态文档。注意:暂时只能生成 swagger2 文档,通过查找源码发现,解析类Swagger20Parser 解析json数据前会判断是否包含 swagger 这个节点,而 swagger3 的 swagger 节点名字已经改成 openapi 了,这个问题暂时未修复,相关问题已经有人提ISSUE Swagger v3 cannot use swagger2markup(1.3.3)

    所以需要生成文档,临时的解决方案是将 new Docket(DocumentationType.OAS_30) 修改为 new Docket(DocumentationType.SWAGGER_2)

    1. 生成 asciidoc 文档 mvn swagger2markup:convertSwagger2markup

      <properties>
      	<swagger2markup.version>1.3.3swagger2markup.version>
      	<asciidoctor.input.directory>${project.basedir}/src/docs/asciidocasciidoctor.input.directory>
          <asciidoctor.html.output.directory>${project.build.directory}/asciidoc/htmlasciidoctor.html.output.directory>
          <asciidoctor.pdf.output.directory>${project.build.directory}/asciidoc/pdfasciidoctor.pdf.output.directory>
      properties> 
      
      <plugin>
          <groupId>io.github.swagger2markupgroupId>
          <artifactId>swagger2markup-maven-pluginartifactId>
          <version>1.3.3version>
          <configuration>
              <swaggerInput>http://localhost:8080/v2/api-docsswaggerInput>
              <outputDir>${asciidoctor.input.directory}outputDir>
              <config>
                  <swagger2markup.markupLanguage>ASCIIDOCswagger2markup.markupLanguage>
              config>
          configuration>
      plugin>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
    2. 生成 HTML 和 PDF 文档 mvn test

      <plugin>
          <groupId>org.asciidoctorgroupId>
          <artifactId>asciidoctor-maven-pluginartifactId>
          <version>1.5.5version>
          <configuration>
              
              <sourceDirectory>${asciidoctor.input.directory}sourceDirectory>
              <headerFooter>trueheaderFooter>
              <doctype>bookdoctype>
              <sourceHighlighter>coderaysourceHighlighter>
              <attributes>
                  
                  <toc>lefttoc>
                  
                  <toclevels>3toclevels>
                  
                  <sectnums>truesectnums>
              attributes>
          configuration>
      
      
          <dependencies>
              <dependency>
                  <groupId>org.asciidoctorgroupId>
                  <artifactId>asciidoctorj-pdfartifactId>
                  <version>1.5.0-alpha.16version>
              dependency>
              <dependency>
                  <groupId>org.jrubygroupId>
                  <artifactId>jruby-completeartifactId>
                  <version>1.7.21version>
              dependency>
          dependencies>
      
          <executions>
              <execution>
                  <id>output-htmlid>
                  <phase>testphase>
                  <goals>
                      <goal>process-asciidocgoal>
                  goals>
                  <configuration>
                      <backend>html5backend>
                      <outputDirectory>${asciidoctor.html.output.directory}outputDirectory>
                  configuration>
              execution>
      
              <execution>
                  <id>output-pdfid>
                  <phase>testphase>
                  <goals>
                      <goal>process-asciidocgoal>
                  goals>
                  <configuration>
                      <backend>pdfbackend>
                      <outputDirectory>${asciidoctor.pdf.output.directory}outputDirectory>
                  configuration>
              execution>
      
          executions>
      plugin>
      
      • 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

    最终在 target 目录下生成

    swagger2_static_doc

    PDF样式如下,可以发现中文有丢失情况,解决方法:swagger+asciidoctor 导出PDF中文缺失乱码问题解决

    swagger2_doc_pdf

    5.4 第三方主题

    推荐一个第三方主题,访问路径为/doc.html

    <dependency>
    	<groupId>com.github.xiaoymingroupId>
    	<artifactId>knife4j-spring-uiartifactId>
    	<version>3.0.3version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    swagger_doc_html

    六、顺序问题(坑)

    3.0版本的各种顺序默认都变成了字母顺序,而且不好更改,是个大坑。介意这个问题的可以考虑换成 springdoc-openapi

    6.1 Controller tag 顺序

    默认字母顺序,理论上讲可以通过 tagsSorter 这个方法修改,但是 TagsSorter 这个枚举也只有 ALPHA(“alpha”) 一个值,所以说还是不能修改

    @Bean
    public UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder()
                .docExpansion(DocExpansion.NONE) // 接口文档展开
                .operationsSorter(OperationsSorter.ALPHA) // http操作排序
                .defaultModelRendering(ModelRendering.EXAMPLE) // Model渲染
                .tagsSorter(TagsSorter.ALPHA)
                .build();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6.2 operation 顺序

    @ApiOperation 中 position 也已经被废弃,但是可以通过 operationsSorter 来进行更改, OperationsSorter 有两种配置,一种是 ALPHA (字母顺序),另一种是 METHOD (根据HTTP方法排序)。这两种排序也还行,但是还是希望可以根据类中方法顺序进行排序。

    @Bean
    public UiConfiguration uiConfig() {
        return UiConfigurationBuilder.builder()
                .docExpansion(DocExpansion.NONE) // 接口文档展开
                .operationsSorter(OperationsSorter.ALPHA) // http操作排序
                .defaultModelRendering(ModelRendering.EXAMPLE) // Model渲染
                .tagsSorter(TagsSorter.ALPHA)
                .build();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    6.3 参数顺序

    自 V2.9 后,参数顺序就变为字母顺序,不能更改,具体 ISSUE: 2.9.0 change the order of parameters in operations。这非常影响查看,比如参数 startTime 和 endTime 就分隔开来了,希望改成类中的字段顺序。

    6.4 model property 顺序

    @ApiModelProperty position 失效,具体 ISSUE : Model properties are alphabetically sorted (without order attribute),维护者说会在v3.0.1 版本修复这个 bug ,但是已经两年没更新,最新版本是 v3.0.0.

    参考

    1. https://en.wikipedia.org/wiki/Swagger_(software)

    2. http://springfox.github.io/springfox/docs/current/#springfox-spring-mvc-and-spring-boot

    3. https://github.com/springfox/springfox-demos

    4. Swagger2 中 paramType

    5. https://github.com/Swagger2Markup/swagger2markup-maven-project-template

    6. swagger+asciidoctor 导出PDF中文缺失乱码问题解决

  • 相关阅读:
    WiFi无缝漫游详解
    学习 Rust 的第十二天:如何使用向量
    TCP与应用层协议
    特斯拉“断网”,数百车主“被锁车外”,马斯克道歉
    视频加密的误解
    记录一次并发情况下的redis导致服务假死的问题
    字符函数和字符串函数
    github push 失败 git did not exit cleanly(exit code 128) 账号登录失败 解决经验
    C++-CMake指令:include指令【.cmake文件/MACRO宏/function函数】
    【无标题】
  • 原文地址:https://blog.csdn.net/qq_23091073/article/details/126127591