Swagger (YAML OpenAPI) 从放弃到入门

Swagger 是一个统一前后端用于生成文档和代码的工具,它使用 yaml / json 作为描述语言 通过 OpenAPI Specification 来描述 API,最后使用 Codegen 根据不同的配置来生成各种 language、library 的 Code、Docs.

其最理想的情况则是只需一份描述文件(yaml/json) 生成 后端、前端(android ios web...)的代码和文档,这样的话保证了前后端的统一,且需要升级改动也只需要修改 yaml 文件。

YAML

JSON 都已经很熟悉了,虽然 Swagger 可以使用 JSON 作为描述语言,但是因为 YAML 更为简洁直观,所以更推荐 YAML。
YAML 的基本语法并不复杂,这里介绍一些基本语法:

  • yaml 文件 以---开始 ...结尾

  • 同一级别的成员(如 list 成员)可以通过"- "来辨识

  • 注释以#开头的一行

  • list:

fruits:
    - Apple
    - Orange
    - Strawberry
    - Mango
  • key / value
martin:
    name: Martin D'vloper
    job: Developer
    skill: Elite
  • list / map 混合使用
-  martin:
    name: Martin D'vloper
    job: Developer
    skills:
      - python
      - perl
      - pascal
-  tabitha:
    name: Tabitha Bitumen
    job: Developer
    skills:
      - lisp
      - fortran
      - erlang
  • list / map 的简写
martin: {name: Martin D'vloper, job: Developer, skill: Elite}
fruits: ['Apple', 'Orange', 'Strawberry', 'Mango']
  • boolean 值的写法没有严格限制
create_key: yes
needs_agent: no
knows_oop: True
likes_emacs: TRUE
uses_cvs: false
  • |使用换行 >忽略换行
include_newlines: |
            exactly as you see
            will appear these three
            lines of poetry

ignore_newlines: >
            this is really a
            single line of text
            despite appearances

OpenAPI-Specification

OpenAPI 是一套用于描述 RESTful APIs 的规范。

The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs which allows both humans and computers to discover and understand the capabilities of the service without access to source code, documentation, or through network traffic inspection. When properly defined, a consumer can understand and interact with the remote service with a minimal amount of implementation logic.

An OpenAPI definition can then be used by documentation generation tools to display the API, code generation tools to generate servers and clients in various programming languages, testing tools, and many other use cases.

只要符合 OpenAPI 规范的都可以生成各类代码、文档、工具。
目前 OpenAPI 最新 3.0.0 ,对比 2.0 对 API 结构进行了调整。
文档内容繁多请参考 OpenAPI-Specification 3.0.0

下面是 Uber API 的 example

# this is an example of the Uber API
# as a demonstration of an API spec in YAML
openapi: "3.0.0"
info:
  title: Uber API
  description: Move your app forward with the Uber API
  version: "1.0.0"
servers:
  - url: https://api.uber.com/v1
paths:
  /products:
    get:
      summary: Product Types
      description: The Products endpoint returns information about the Uber products offered at a given location. The response includes the display name and other details about each product, and lists the products in the proper display order.
      parameters:
        - name: latitude
          in: query
          description: Latitude component of location.
          required: true
          schema:
            type: number
            format: double
        - name: longitude
          in: query
          description: Longitude component of location.
          required: true
          schema:
            type: number
            format: double
      security: 
        - apikey: []
      tags: 
        - Products
      responses:  
        '200':
          description: An array of products
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ProductList"
        default:
          description: Unexpected error
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
  /estimates/price:
    get:
      summary: Price Estimates
      description: The Price Estimates endpoint returns an estimated price range for each product offered at a given location. The price estimate is provided as a formatted string with the full price range and the localized currency symbol.

The response also includes low and high estimates, and the [ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code for situations requiring currency conversion. When surge is active for a particular product, its surge_multiplier will be greater than 1, but the price estimate already factors in this multiplier. parameters: - name: start_latitude in: query description: Latitude component of start location. required: true schema: type: number format: double - name: start_longitude in: query description: Longitude component of start location. required: true schema: type: number format: double - name: end_latitude in: query description: Latitude component of end location. required: true schema: type: number format: double - name: end_longitude in: query description: Longitude component of end location. required: true schema: type: number format: double tags: - Estimates responses: '200': description: An array of price estimates by product content: application/json: schema: type: array items: $ref: "#/components/schemas/PriceEstimate" default: description: Unexpected error content: application/json: schema: $ref: "#/components/schemas/Error" /estimates/time: get: summary: Time Estimates description: The Time Estimates endpoint returns ETAs for all products offered at a given location, with the responses expressed as integers in seconds. We recommend that this endpoint be called every minute to provide the most accurate, up-to-date ETAs. parameters: - name: start_latitude in: query description: Latitude component of start location. required: true schema: type: number format: double - name: start_longitude in: query description: Longitude component of start location. required: true schema: type: number format: double - name: customer_uuid in: query schema: type: string format: uuid description: Unique customer identifier to be used for experience customization. - name: product_id in: query schema: type: string description: Unique identifier representing a specific product for a given latitude & longitude. tags: - Estimates responses: '200': description: An array of products content: application/json: schema: type: array items: $ref: "#/components/schemas/Product" default: description: Unexpected error content: application/json: schema: $ref: "#/components/schemas/Error" /me: get: summary: User Profile description: The User Profile endpoint returns information about the Uber user that has authorized with the application. tags: - User responses: '200': description: Profile information for a user content: application/json: schema: $ref: "#/components/schemas/Profile" default: description: Unexpected error content: application/json: schema: $ref: "#/components/schemas/Error" /history: get: summary: User Activity description: The User Activity endpoint returns data about a user's lifetime activity with Uber. The response will include pickup locations and times, dropoff locations and times, the distance of past requests, and information about which products were requested.

The history array in the response will have a maximum length based on the limit parameter. The response value count may exceed limit, therefore subsequent API requests may be necessary. parameters: - name: offset in: query schema: type: integer format: int32 description: Offset the list of returned results by this amount. Default is zero. - name: limit in: query schema: type: integer format: int32 description: Number of items to retrieve. Default is 5, maximum is 100. tags: - User responses: '200': description: History information for the given user content: application/json: schema: $ref: "#/components/schemas/Activities" default: description: Unexpected error content: application/json: schema: $ref: "#/components/schemas/Error" components: securitySchemes: apikey: type: apiKey name: server_token in: query schemas: Product: properties: product_id: type: string description: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles. description: type: string description: Description of product. display_name: type: string description: Display name of product. capacity: type: integer description: Capacity of product. For example, 4 people. image: type: string description: Image URL representing the product. ProductList: properties: products: description: Contains the list of products type: array items: $ref: "#/components/schemas/Product" PriceEstimate: properties: product_id: type: string description: Unique identifier representing a specific product for a given latitude & longitude. For example, uberX in San Francisco will have a different product_id than uberX in Los Angeles currency_code: type: string description: "[ISO 4217](http://en.wikipedia.org/wiki/ISO_4217) currency code." display_name: type: string description: Display name of product. estimate: type: string description: Formatted string of estimate in local currency of the start location. Estimate could be a range, a single number (flat rate) or "Metered" for TAXI. low_estimate: type: number description: Lower bound of the estimated price. high_estimate: type: number description: Upper bound of the estimated price. surge_multiplier: type: number description: Expected surge multiplier. Surge is active if surge_multiplier is greater than 1. Price estimate already factors in the surge multiplier. Profile: properties: first_name: type: string description: First name of the Uber user. last_name: type: string description: Last name of the Uber user. email: type: string description: Email address of the Uber user picture: type: string description: Image URL of the Uber user. promo_code: type: string description: Promo code of the Uber user. Activity: properties: uuid: type: string description: Unique identifier for the activity Activities: properties: offset: type: integer format: int32 description: Position in pagination. limit: type: integer format: int32 description: Number of items to retrieve (100 max). count: type: integer format: int32 description: Total number of items available. history: type: array items: $ref: "#/components/schemas/Activity" Error: properties: code: type: string message: type: string fields: type: string

这里将 OpenAPI 3.0 规范中把重点使用的 api 整理了一张思维导图(还不完善,会持续更新)

Swagger (YAML OpenAPI) 从放弃到入门_第1张图片
OpenAPI 3.0.0.png

Swagger

Swagger 实际上包含了一系列的工具 Editor Codegen UI ...

  • Editor 用于使用 OpenAPI 编辑 yaml
  • Codegen 用于生成不同的 language library 的代码
  • UI 用于生成文档

下面介绍一下 Codegen 的使用:

  1. 首先下载 swagger-codegen-cli.jar

  2. 确保装好了 maven

  3. 准备 swagger.yaml

  4. 编写 config.json 配置文件
    因为之前提到 Swagger 可以生成各种 lang lib 的代码,所以这里便是进行此类配置:
    查看配置 help

    java -jar swagger-codegen-cli.jar config-help -l java
    
    CONFIG OPTIONS
         sortParamsByRequiredFlag
             Sort method arguments to place required parameters before optional parameters. (Default: true)
         ensureUniqueParams
             Whether to ensure parameter names are unique in an operation (rename parameters that are not). (Default: true)
         allowUnicodeIdentifiers
             boolean, toggles whether unicode identifiers are allowed in names or not, default is false (Default: false)
         modelPackage
             package for generated models
         apiPackage
             package for generated api classes
         invokerPackage
             root package for generated code
         groupId
             groupId in generated pom.xml
         artifactId
             artifactId in generated pom.xml
         artifactVersion
             artifact version in generated pom.xml
         artifactUrl
             artifact URL in generated pom.xml
         artifactDescription
             artifact description in generated pom.xml
         scmConnection
             SCM connection in generated pom.xml
         scmDeveloperConnection
             SCM developer connection in generated pom.xml
         scmUrl
             SCM URL in generated pom.xml
         developerName
             developer name in generated pom.xml
         developerEmail
             developer email in generated pom.xml
         developerOrganization
             developer organization in generated pom.xml
         developerOrganizationUrl
             developer organization URL in generated pom.xml
         licenseName
             The name of the license
         licenseUrl
             The URL of the license
         sourceFolder
             source folder for generated code
         localVariablePrefix
             prefix for generated code members and local variables
         serializableModel
             boolean - toggle "implements Serializable" for generated models (Default: false)
         bigDecimalAsString
             Treat BigDecimal values as Strings to avoid precision loss. (Default: false)
         fullJavaUtil
             whether to use fully qualified name for classes under java.util. This option only works for Java API client (Default: false)
         hideGenerationTimestamp
             hides the timestamp when files were generated
         withXml
             whether to include support for application/xml content type. This option only works for Java API client (resttemplate) (Default: false)
         dateLibrary
             Option. Date library to use
                 joda - Joda (for legacy app only)
                 legacy - Legacy java.util.Date (if you really have a good reason not to use threetenbp
                 java8-localdatetime - Java 8 using LocalDateTime (for legacy app only)
                 java8 - Java 8 native JSR310 (preferred for jdk 1.8+) - note: this also sets "java8" to true
                 threetenbp - Backport of JSR310 (preferred for jdk < 1.8)
         java8
             Option. Use Java8 classes instead of third party equivalents
                 true - Use Java 8 classes such as Base64
                 false - Various third party libraries as needed
         useRxJava
             Whether to use the RxJava adapter with the retrofit2 library. (Default: false)
         useRxJava2
             Whether to use the RxJava2 adapter with the retrofit2 library. (Default: false)
         parcelableModel
             Whether to generate models for Android that implement Parcelable with the okhttp-gson library. (Default: false)
         usePlay24WS
             Use Play! 2.4 Async HTTP client (Play WS API) (Default: false)
         supportJava6
             Whether to support Java6 with the Jersey1 library. (Default: false)
         useBeanValidation
             Use BeanValidation API annotations (Default: false)
         performBeanValidation
             Perform BeanValidation (Default: false)
         useGzipFeature
             Send gzip-encoded requests (Default: false)
         useRuntimeException
             Use RuntimeException instead of Exception (Default: false)
         library
             library template (sub-template) to use (Default: okhttp-gson)
                 jersey1 - HTTP client: Jersey client 1.19.4. JSON processing: Jackson 2.8.9. Enable Java6 support using '-DsupportJava6=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
                 feign - HTTP client: OpenFeign 9.4.0. JSON processing: Jackson 2.8.9
                 jersey2 - HTTP client: Jersey client 2.25.1. JSON processing: Jackson 2.8.9
                 okhttp-gson - HTTP client: OkHttp 2.7.5. JSON processing: Gson 2.8.1. Enable Parcelable models on Android using '-DparcelableModel=true'. Enable gzip request encoding using '-DuseGzipFeature=true'.
                 retrofit - HTTP client: OkHttp 2.7.5. JSON processing: Gson 2.3.1 (Retrofit 1.9.0). IMPORTANT NOTE: retrofit1.x is no longer actively maintained so please upgrade to 'retrofit2' instead.
                 retrofit2 - HTTP client: OkHttp 3.8.0. JSON processing: Gson 2.6.1 (Retrofit 2.3.0). Enable the RxJava adapter using '-DuseRxJava[2]=true'. (RxJava 1.x or 2.x)
                 resttemplate - HTTP client: Spring RestTemplate 4.3.9-RELEASE. JSON processing: Jackson 2.8.9
                 resteasy - HTTP client: Resteasy client 3.1.3.Final. JSON processing: Jackson 2.8.9
    
    

    几个主要的配置参数:

    • library,生成的代码支付的类,有jersey1、jersey2、okhttp-gson、resttemplate、resteasy、feign、retrofit、retrofit2等几种类型,我们选择的retrofit2
    • developerName,开发者名字,会出现在代码文件里
    • developerEmail,开发者邮箱,会出现在代码文件里
    • developrOrganization,开发者组织,会出现在代码里
    • invokerPackage,项目的包名
    • apiPackage,生成的***Api.java文件的包名
    • modelPackage,生成的数据模型java文件包名
    • dateLibrary,时间使用的类开
    • useRxJava,是否使用rxjava生成api接口
    • useRxJava2,是否使用rxjava2的方式调用接口
  5. generate 生成代码
    首先打印参数信息

    java -jar swagger-codegen-cli.jar generate help
    
    NAME
         swagger-codegen-cli generate - Generate code with chosen lang
     SYNOPSIS
         swagger-codegen-cli generate
                 [(-a  | --auth )]
                 [--additional-properties ...]
                 [--api-package ] [--artifact-id ]
                 [--artifact-version ]
                 [(-c  | --config )]
                 [-D ...] [--git-repo-id ]
                 [--git-user-id ] [--group-id ]
                 [--http-user-agent ]
                 (-i  | --input-spec )
                 [--ignore-file-override ]
                 [--import-mappings ...]
                 [--instantiation-types ...]
                 [--invoker-package ]
                 (-l  | --lang )
                 [--language-specific-primitives ...]
                 [--library ] [--model-name-prefix ]
                 [--model-name-suffix ]
                 [--model-package ]
                 [(-o  | --output )]
                 [--release-note ] [--remove-operation-id-prefix]
                 [--reserved-words-mappings ...]
                 [(-s | --skip-overwrite)]
                 [(-t