Swagger RESTful API的定义与生成

Swagger是当前比较受欢迎的API工具,它遵循 OpenAPI Specification(OpenAPI 规范,也简称 OAS)。

Swagger项目由曾担任 Wordnik 首席技术官的 Tony Tam 于 2011 年启动,在 2015 年由 SmartBear 公司收购,目前仍由 SmartBear 公司维护,Swagger 项目源码可以从 GitHub 下载。

Swagger项目工具集

Swagger Codegen:它可以将 RESTful API 描述文件生成 30 多个 API 客户端的代码,25 个服务器存根的代码和 API 文档。Swagger Codegen 使用的是 Contract First 的方法,API 所有者只需要创建或更新 RESTful API 描述文件(YAML 或 JSON 格式),然后利用 Swagger Codegen 生成各种编程语言的 SDK 和对应的 API 文档。

下文将详细介绍如何使用 Swagger Codegen。

Swagger Editor:

编辑 RESTful API 描述文件的编辑器,该编辑器支持实时预览描述文件的更新效果。

Swagger Editor:

提供了在线和本地部署两种方式。Swagger Editor 可以验证 RESTful API 描述文件是否符合 Swagger 规范,且支持不同格式的转换(YAML 转 JSON,或 JSON 转 YAML)。

具体例子参考:

http://editor.swagger.io/。

此外,Swagger Editor 支持 RESTful API 描述文件从 Swagger 2.0 规范迁移到 OpenAPI 3.0 规范,比如在 Swagger Editor 中输入 Swagger 2.0 规范格式的内容,然后选择菜单中 Edit,点击 Convert to OpenAPI 3,即可自动完成不同版本的规范的转换。

Swagger UI:

一个交互式的在线查看器,用于在浏览器中快速的查看 RESTful API。

例子可以参考

https://petstore.swagger.io

Swagger UI 支持在线和本地部署两种方式。

可以利用 Swagger UI 的 Try it out 生成 RESTful Service 的简单的 Request Sample,以快速的调试 API 程序。

SwaggerHub:

一种基于 OpenAPI 规范的 API 设计和文档化的平台。它集成了上面所有开发工具的各个功能,它需要会员注册,分为免费版本和收费版本。

Swagger Inspector:

类似 postman 和 SoapUI,是 RESTful API 的测试工具。它只有收费版本。

此外 Springfox 虽不属于 Swagger 项目,但它是 OpenAPI 规范的一个 Spring 实现。

通过 Swagger 注解生成接口文档,Swagger 注解包括接口名、请求方法、参数、返回信息等等,并以在线方式展示 API 的信息。Swagger Codegen 也可以生成 Springfox 的相关简单代码。

基于 OpenAPI 规范定义 RESTful API

开发 RESTful API 描述文件时,需要保证描述文件符合一定的规范。

在用 Swagger 开发时,API 描述文件需要遵守 OpenAPI 规范。OpenAPI 规范现在是 Linux 基金会的一个项目,其目标是为 RESTful API 定义一个标准的、与语言无关的接口,允许人和计算机在不访问源代码、额外的文档或通过网络的情况下发现和理解服务的功能。

支持两种格式:YAML 和 JSON。

本文将以 YAML 格式为主详细介绍 Swagger 的使用。

OpenAPI 规范在版本 2.0 时,名字是 Swagger 规范,后来版本升级为 3.0 之后,更名为 OpenAPI 规范。Swagger 2.0 规范和 OpenAPI 3.0 规范具体细节可以参考本文的参考资源。

OpenAPI 3.0 规范吸纳了开发人员的反馈建议,修正了 Swagger 2.0 规范的一些 bug,并对 Swagger 2.0 规范进行了一定的优化,同时也增加了一些重要的新特性和新内容,

如下图所示:

使用 Swagger Codegen 生成 RESTful API

Swagger Codegen 是 RESTful API 代码生成器,它的源码放在

https://github.com/swagger-api/swagger-codegen

可以通过多种方式调用 Swagger Codegen,比如命令行方式

Docker,swagger-codegen-maven-plugin 或者 Gradle Swagger Generator Plugin,

也可以在线生成

https://editor.swagger.io/

它可以生成多种语言或框架的 API 的客户端,比如 JavaScript,Java,Spring 的 API 的客户端代码。

本文侧重 Swagger 在 Spring 中的应用,并以常用 swagger-codegen-maven-plugin 调用方式来生成 Spring 的 API 的客户端。

基于 Swagger 2.0 规范和 OpenAPI 3.0 规范生成 Spring 的 API 客户端的基本配置相同,只有某些细节不尽相同。

基于 Swagger 2.0 规范生成 Spring 的 API 客户端

基于 Swagger 2.0 规范具体配置如下:

<plugin>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-codegen-maven-plugin</artifactId>
  <version>2.2.3</version>
  <executions>
    <execution>
      <id>account</id>
      <goals>
        <goal>generate</goal>
      </goals>
<configuration>       <inputSpec>${basedir}/src/main/resources/v2.yaml</inputSpec>
        <language>spring</language>
        <environmentVariables>
          <models></models>
          <apis></apis>
        </environmentVariables>                  <generateApis>true</generateApis>
        <generateSupportingFiles>false</generateSupportingFiles>
        <modelPackage>com.example.model</modelPackage>
        <configOptions>
          <dateLibrary>java8</dateLibrary>
          <serializableModel>true</serializableModel>
        </configOptions>
      </configuration>
    </execution>
  </executions>
</plugin>

注意:当 dateLibrary 为 java8,日期类型生成 Java 8 的 java.time.LocalDate;而 dateLibrary 为 joda 时,日期类型生成 Joda 的 org.joda.time.LocalDate,默认的 dateLibrary 为 joda。

当 language 设定为 Spring 并且在 environmentVariables 配置 <apis></apis>,通过 Maven 的 generate-resources 或 install 命令,进而调用 swagger-codegen-maven-plugin,就可以生成 API 的客户端以及相关的 Bean 类。

若想除第一次生成 API 的客户端,之后不再生成 API 的客户端,只需要将 generateApis 的值改为 false,这样每次只会生成相关的 Bean 类。

API 的客户端以及相关的 Bean 类需要引入依赖包,这些依赖包需要加入到相应的 pom.xml。

Spring 相关依赖包

<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-core</artifactId>
  <version>1.6.0</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>

基于 OpenAPI 3.0 规范生成 Spring 的 API 客户端

上面的配置是针对 Swagger 2.0 规范,若 YAML 文件遵循 OpenAPI 3.0 规范,swagger-codegen-maven-plugin 配置和依赖包也需要变化。

swagger-codegen-maven-plugin 的 groupId 和 version 变化为 v3 相应版本,inputSpec 为 OpenAPI 3.0 规范的文件。

OpenAPI 3.0 规范中 swagger-codegen-maven-plugin 配置

<plugin>
  <groupId>io.swagger.codegen.v3</groupId>
  <artifactId>swagger-codegen-maven-plugin</artifactId>
  <version>3.0.11</version>
  <executions>
    <execution>
      <id>account</id>
      <goals>
        <goal>generate</goal>
      </goals>
<configuration>
<inputSpec>${basedir}/src/main/resources/v3.yaml
</inputSpec>
        <language>spring</language>
        .......
      </configuration>
    </execution>
  </executions>
</plugin>

基于 OpenAPI 3.0 规范生成的 API 的客户端需要额外引入 servlet-api 包,这点与基于 Swagger 2.0 规范生成的 API 的客户端不同具体设置如下。

OpenAPI 3.0 规范中相关依赖包

<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-core</artifactId>
  <version>1.6.0</version>
</dependency>
<dependency>
  <groupId>io.swagger</groupId>
  <artifactId>swagger-annotations</artifactId>
  <version>1.6.0</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>servlet-api</artifactId>
  <version>2.5</version>
  <scope>provided</scope>
</dependency>

与 Spring Validation 的整合进行代码校验

在开发 Restful API 的过程中, 往往需要对 Request 的元素做无业务关联的规则性验证,然后这些验证结果无误后,才会进入后续的业务处理。无业务关联的规则性验证包括非空,非 null,整数值的范围校验等等。这部分代码校验工作比较繁琐,费时费力,也很容易出错。

swagger-codegen 可以在生成代码时,自动生成注解,这样再结合 Spring Validation 以及 Hibernate Validator, 就可以基于 Swagger 规则的基本校验。

Spring Validation 遵守 Java 的校验规范 JSR-303(javax.validator.Validator)。它需要校验规范 JSR-303 的具体实现,Hibernate validator 是它的一个实现。

而 Spring Boot 会自动包含 Hibernate Validator,这样当系统使用 Spring Boot 开发,不需要额外引入其他依赖包。

下图描述了 Spring Boot,Spring Validation,Hibernate Validator 的调用关系。其中 ValidatorImpl 调用 MinValidatorForNumber 的 isValid,为了简化,略去一些中间不重要的环节。

其中:

ValidatorAdapter:来自 spring-boot-autoconfigure 包

SpringValidatorAdapter:来自 spring-context 包,它将 Hibernate Validator 注入到 Spring Boot 环境中。

ValidatorImpl:来自 hibernate-validator 包,它实现 validation-api-1.1.0 包下的 javax.validator.Validator 接口。

MetaConstraint:来自 hibernate-validator 包。

MinValidatorForNumber:来自 hibernate-validator 包,具体校验逻辑在这个类完成。

在 Swagger 的 YAML 文件中的校验规则定义和由 swagger-codegen 生成的 Java 注解的对应关系,如下表所示。

Swagger 中的校验规则和生成的 Java 注解

元素类别 Swagger 中的校验规则定义 生成的 Java 注解(遵循 JSR-303 的规则)

其中在 YAML 文件中 pattern 需要符合 ECMA 262 正则表达式的规范。

利用 Spring Boot 和 swagger-codegen,无需做太多工作,只需要添加校验不通过的处理的代码。下面是一个简单的校验不通过的处理的代码例子。

YAML 文件:

Account:
 type: object
 properties:
  accountId:
    type: integer
    format: int32
    minimum: 1000

代码:

ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler {

  // error handle for @Valid
  @Override
  protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex,HttpHeaders headers,HttpStatus status, WebRequest request) {
    Map<String, Object> body = new LinkedHashMap<>();
    body.put("timestamp", new Date());
    body.put("status", status.value());
    //Get all errors
    List<String> errors = ex.getBindingResult()
        .getFieldErrors()
        .stream()
        .map(x -> x.getField() + " " + x.getDefaultMessage())
        .collect(Collectors.toList());
    body.put("errors", errors);
    return new ResponseEntity<>(body, headers, status);
  }
}

上面所述应用于 Spring Boot 应用程序。而对于 Sping MVC 应用程序,就稍微麻烦些,需要额外执行下面的步骤。

在 Spring 的配置文件中加入 MethodValidationPostProcessor。

<bean 
class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"> 
<property name="validatedAnnotationType" value="javax.validation.Valid" /> 
</bean>

在 Controller 的输入参数,加入 @Valid,比如 @RequestBody AccountRequest body。

添加 Hibernate Validator 的依赖包到 pom.xml 文件中。

注意事项

用好Swagger Editor

将写好的 YAML 文件内容复制到 Swagger Editor 中,若有格式错误或不符合规范,Swagger Editor 会给出相应的错误提示,根据这些提示,就可以快速定位问题进而修正。

swagger-codegen 在兼容方面做得不是很好,不同版本生成的代码会有些不同。比如对于 YAML 文件中的 required 为 false 的 Array:

produces 和 consumes 的设置

若在遵循 Swagger 2.0 规范的 YAML 文件中,produces 和 consumes 应用于所有的 path,swagger-codegen 生成 API 代码的所有 method 都会包含 produces 和 consumes。但由于 get 的 method 中 request 没有 body,API 代码不应该包含 consumes,这时需要在 API 代码中手动删除 consumes,另一种的解决方案是单独设置每个 path 的 produces 和 consumes。在遵循 OpenAPI 3.0 规范的 YAML 文件文件中,produces 和 consumes 只能应用于单个的 path,可以避免这个问题。