Swagger 框架核心的框架包含三个核心工具:Swagger EditorSwagger CodegenSwagger UI。本文介绍如何使用这三个工具来快速构建 Spring Boot Server。

Editor 用于编辑 Swagger 规格文档,并实时生成对应的 RESTful 接口文档。

Codegen 用于将设计好的 Swagger 规格文档生成不同框架的代码,包括服务端和客户端。

UI 用于将 Swagger 规格文档进行显示以及与 API 资源进行交互。

那这三个工具应该如何使用可能困扰着一些刚接触 Swagger 的开发人员,本文简单介绍一下如何将 Swagger 用于 Spring Boot 框架。

首先,Editor 和 Codegen 这两个工具个人认为并不需要自己安装,直接在 Swagger 的 Online Editor 进行 API 的设计和编辑,设计好之后使用界面上侧的 Generate Server 选择相应的框架进行生成,如图:

Codegen

将生成的包解压,并将src/main/resources/application.properties 中的 server.port 改为一个空闲的端口号(这里假设为8181)。使用 maven 生成可执行 jar 包。

→ ~/work/myprojects/spring-server $ mvn package
→ ~/work/myprojects/spring-server $ cd target/
# 运行 Spring Boot 程序
→ ~/work/myprojects/spring-server/target $ java -jar swagger-spring-1.0.0.jar

浏览器访问 http://localhost:8181/v1/swagger-ui.html 即可使用 Swagger UI 来消费你的 API 了, 当然,对于任何访问,这个生成的服务器并没有做任何处理,仅仅返回一个状态为 200 的空值,这就是你需要发威的地方了。

代码地址:spring-server

我们来看代码,代码结构:

→ ~/work/myprojects/spring-server (master) $ tree
.
├── README.md
├── pom.xml
├── src
│   └── main
│       ├── java
│       │   └── io
│       │       └── swagger
│       │           ├── RFC3339DateFormat.java
│       │           ├── Swagger2SpringBoot.java                 # Spring Boot 启动程序
│       │           ├── api                                     # 该文件下为定义的接口和实现
│       │           │   ├── ApiException.java
│       │           │   ├── ApiOriginFilter.java
│       │           │   ├── ApiResponseMessage.java
│       │           │   ├── EstimatesApi.java                   # Api结尾的为Interface
│       │           │   ├── EstimatesApiController.java         # Controller结尾的为Implementation
│       │           │   ├── HistoryApi.java
│       │           │   ├── HistoryApiController.java
│       │           │   ├── MeApi.java
│       │           │   ├── MeApiController.java
│       │           │   ├── NotFoundException.java
│       │           │   ├── ProductsApi.java
│       │           │   └── ProductsApiController.java
│       │           ├── configuration
│       │           │   ├── HomeController.java
│       │           │   └── SwaggerDocumentationConfig.java     # Swagger 文档配置
│       │           └── model                                   # 定义的model 
│       │               ├── Activities.java
│       │               ├── Activity.java
│       │               ├── Error.java
│       │               ├── PriceEstimate.java
│       │               ├── Product.java
│       │               └── Profile.java
│       └── resources
│           └── application.properties
└── swagger-spring.iml

Spring Boot 启动程序: Swagger2SpringBoot.java

package io.swagger;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

import springfox.documentation.swagger2.annotations.EnableSwagger2;

@SpringBootApplication
@EnableSwagger2         // 用于生成Swagger文档
@ComponentScan(basePackages = "io.swagger")
public class Swagger2SpringBoot implements CommandLineRunner {

    @Override
    public void run(String... arg0) throws Exception {
        if (arg0.length > 0 && arg0[0].equals("exitcode")) {
            throw new ExitException();
        }
    }

    public static void main(String[] args) throws Exception {
        new SpringApplication(Swagger2SpringBoot.class).run(args);
    }

    class ExitException extends RuntimeException implements ExitCodeGenerator {
        private static final long serialVersionUID = 1L;

        @Override
        public int getExitCode() {
            return 10;
        }

    }
}

Swagger 文档配置

package io.swagger.configuration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;

@javax.annotation.Generated(value = "io.swagger.codegen.languages.SpringCodegen", date = "2017-03-28T06:06:39.062Z")

@Configuration
public class SwaggerDocumentationConfig {

    ApiInfo apiInfo() {
        return new ApiInfoBuilder()
            .title("Uber API")
            .description("Move your app forward with the Uber API")
            .license("")
            .licenseUrl("http://unlicense.org")
            .termsOfServiceUrl("")
            .version("1.0.0")
            .contact(new Contact("","", ""))
            .build();
    }

    @Bean
    public Docket customImplementation(){
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                    .apis(RequestHandlerSelectors.basePackage("io.swagger.api"))    // RESTful Controller 所在的目录
                    .build()
                .directModelSubstitute(org.joda.time.LocalDate.class, java.sql.Date.class)
                .directModelSubstitute(org.joda.time.DateTime.class, java.util.Date.class)
                .apiInfo(apiInfo());
    }

}

可以看出使用了 springfox 来生成 Swagger 规格文档,如果看 pom.xml 文件,你会发现生成的代码使用了 springfox-swagger2 和 springfox-swagger-ui,笔者的理解是,springfox-swagger2 用于将程序中的 Swagger annotation 生成 Swagger 规格文档,而 springfox-swagger-ui 则用于将规格文档进行展现并处理用户在 UI 上对 API 资源的操作。更多 Springfox 相关的内容参见 Springfox Reference Documentation

Home Controller

package io.swagger.configuration;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * Home redirection to swagger api documentation
 */
@Controller
public class HomeController {
    @RequestMapping(value = "/")    // 将 basePath/重定向到 swagger-ui.html
    public String index() {
        System.out.println("swagger-ui.html");
        return "redirect:swagger-ui.html";
    }
}

Spring Annotations

Swagger 包含了一些特定的注解,可以更好的显示API:

@API               表示一个开放的API,可以通过description简要描述该API的功能,一般和 Spring 的 @Controller一起。
@ApiOperation      在一个@API下,可有多个@ApiOperation,表示针对该API的CRUD操作。在ApiOperation
                   中可以通过value,notes描述该操作的作用,response描述正常情况下该请求的返回对象类型。
@ApiResponse       在一个ApiOperation下,可以通过ApiResponses描述该API操作可能出现的异常情况。
@ApiParam          描述该API操作接受的参数类型
@ApiModel          描述封装的参数对象与返回的参数对象
@ApiModelProperty  描述ApiModel的属性

总结

有人喜欢将 Swagger 规格文档单独存放在项目中,内嵌一个 Swagger UI, 并将 index.html 中的 url 指向规格文档,如 ref2。笔者认为这种方式不是特别利于维护,假设代码修改了,而忘了修改 Swagger 规格文档,则会造成不一致。本文使用的 Swagger 注解的方式使得文档跟着代码一块走,更利于文档的维护。