Swagger使用初探

簡介

Swagger是什麼?在解答這個問題之前,我們先來看看開發Restful API過程中會遇到什麼問題。我們如何去設計和定義API接口?如何寫接口文檔?是否可以自動生成接口文檔,或者設計好API之後自動生成接口代碼?如果有那麼一個工具可以幫助我們實現上述功能,是否感覺很棒。是的,Swagger就是幹這個的,而且通過Swagger生成的接口文檔不僅非常漂亮,還能實現交互測試的功能。
Swagger實際上是通過一系列工具來實現這些功能的。其主要工具如下:

  • Swagger-editor : 一個用於編輯API文檔的工具
  • Swagger-ui: 用於解析editor生成的json文件,並生成對應的API文檔的頁面,並可以展示頁面的web應用
  • Swagger-core: Swagger的Java/Scala實現,並已集成 JAX-RS (Jersey, Resteasy, CXF...), Servlets與Play Framework (其實不懂,有明白的網友歡迎留言)
  • Swagger-codegen: 用於自動生成客戶端和服務器端的代碼

我目前使用的主要是swagger-editor和swagger-ui,用於生成API文檔。下面先介紹這兩個工具。

Swagger-Editor

swagger-editor主要用於在設計和定義restful api的時候使用。我們通過編輯YAML配置文件,從而可以自動顯示出API文檔,從而允許我們先將接口定義清楚。

Swagger-Editor安裝

  1. 下載Swagger-Editor。直接下載zip文件地址並解壓
  2. 下載並且安裝node.js
  3. npm install -g http-server
  4. 進入swagger-editor文件夾。運行http-server命令。
  5. 在瀏覽器中輸入http://127.0.0.1:8080即可看到Swagger-Editor的界面

YAML語言

關於YAML語言可以參考阮一峯老師的YAML 語言教程。簡單來說,我們可以通過YAML來實現某種類似於json的數據表達和描述的方式。但是它跟json相比較而言,可以有更豐富的信息表達,比如可以描述數據的類型,可以添加更多的文檔相關的信息(這一點類似於xml,但是yaml要比xml簡潔的多,當然代價是不能像xml那樣可以通過dtd文件來驗證其正確性)。另外,大部分語言都有YAML語言的相關實現,比如在Python中我們通過安裝pyyaml或者ruamel.yaml(支持yaml1.2)來實現對YAML文檔的讀寫。

OpenAPI Specification

YAML只是一種數據表示,我們還必須通過一定的規範才能描述API接口文檔(比如定義path用來描述路徑),而這個規範就是OpenAPI Specification。目前最新的版本是3.x版本(文檔開頭會是openapi: 3.0.0),而在Swagger的很多例子當中我們看到的是2.0的版本(也就是通常在文件開頭的swagger: 2.0)。這一點是需要注意的,因爲不同的版本的spec,其語法並不相同,如果搞混就會報語法錯誤。

OpenAPI Spec文檔可以參考其github地址。我們以3.0版本爲例,先上一個例子:

# 文本開頭必須先定義openapi版本,必填項
openapi: 3.0.0

# 文檔的meta data,顯示文檔的一些基本信息,必填項
info:
  title: Sample API
  description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML.
  version: 0.1.9

# 定義server相關信息,非必填
servers:
  - url: http://api.example.com/v1
    description: Optional server description, e.g. Main (production) server
  - url: http://staging-api.example.com
    description: Optional server description, e.g. Internal staging server for testing

# API的具體描述,必填項
paths:
  /users:  # api path
    get:  # api method
      summary: Returns a list of users.
      description: Optional extended description in CommonMark or HTML.
      responses:  # define response
        '200':    # status code
          description: A JSON array of user names
          content:
            application/json:
              schema: 
                type: array
                items: 
                  type: string
  /user/{userId}:
    get:
      summary: Returns a user by ID.
      parameters:  # 定義參數
        - name: userId
          in: path
          required: true
          description: Parameter description in CommonMark or HTML.
          schema:
            type : integer
            format: int64
            minimum: 1
      responses: 
        '200':
          description: OK

對照上邊的例子和下表,我們可以看到OpenAPI spec是如何定義一個接口的。此種定義方式非常細緻和繁瑣,這主要是因爲Swagger需要根據這些細緻地描述來生成相對應的代碼,所以就有必要將所有的細節都描述清楚。

Field Name Type Description
openapi string REQUIRED. This string MUST be the semantic version number of the OpenAPI Specification version that the OpenAPI document uses. The openapi field SHOULD be used by tooling specifications and clients to interpret the OpenAPI document. This is not related to the API info.version string.
info Info Object REQUIRED. Provides metadata about the API. The metadata MAY be used by tooling as required.
servers [Server Object] An array of Server Objects, which provide connectivity information to a target server. If the servers property is not provided, or is an empty array, the default value would be a Server Object with a url value of /.
paths Paths Object REQUIRED. The available paths and operations for the API.
components Components Object An element to hold various schemas for the specification.
security [Security Requirement Object] A declaration of which security mechanisms can be used across the API. The list of values includes alternative security requirement objects that can be used. Only one of the security requirement objects need to be satisfied to authorize a request. Individual operations can override this definition.
tags [Tag Object] A list of tags used by the specification with additional metadata. The order of the tags can be used to reflect on their order by the parsing tools. Not all tags that are used by the Operation Object must be declared. The tags that are not declared MAY be organized randomly or based on the tools' logic. Each tag name in the list MUST be unique.
externalDocs External Documentation Object Additional external documentation.

通過Swagger-Editor我們可以編輯YAML,同時直接在右端顯示相應的API文檔,如下圖:


通過[File -> Convert and save as JSON], 我們可以將該YAML轉換成json文件,而該json文件可以通過Swagger-UI來解析並展示成API文檔,同時,通過Swagger-UI我們還可以執行相應的http request測試。

Swagger-UI

Swagger-UI安裝

  1. 下載Swagger-UI。直接下載zip文件地址並解壓
  2. 下載並且安裝node.js
  3. npm install -g http-server
  4. 進入swagger-editor文件夾。運行http-server -p 8081命令。
  5. 在瀏覽器中輸入http://127.0.0.1:8081即可看到Swagger-UI展示的界面

配置

  1. 首先將Swagger-Editor生成的json文件拷貝到swagger-ui主目錄下的dist文件夾
  2. 修改index.html中的url,假設你的json文件名爲swagger.json,那麼修改url如下:
 <script>
    window.onload = function() {
      // Begin Swagger UI call region
      const ui = SwaggerUIBundle({
        # 主要是修改這裏的url,以對應你在dist目錄下的json文件
        url: "http://127.0.0.1:8081/dist/swagger.json",
        dom_id: '#swagger-ui',
        deepLinking: true,
        presets: [
          SwaggerUIBundle.presets.apis,
          SwaggerUIStandalonePreset
        ],
        plugins: [
          SwaggerUIBundle.plugins.DownloadUrl
        ],
        layout: "StandaloneLayout"
      })
      // End Swagger UI call region

      window.ui = ui
    }
  </script>
  1. 重新啓動Swagger-UI
# 注意端口必須要與url中設定的端口一致,否則會有跨域問題
http-server -p 8081

這時,就可以看到Swagger-UI解析生成的API文檔了

通過Nginx展示靜態頁面

當我們需要對外部展示API文檔的時候,我們可以通過redoc-cli來生成靜態的html頁面。步驟如下:

  1. npm install -g redoc-cli
  2. 有時redoc-cli無法直接使用, 需要安裝npx.
    npm install -g npx
    注意: 有些系統(比如Ubuntu),其安裝位置爲/opt目錄下,我們需要將其添加到path當中或者軟鏈接到/usr/bin下
  3. 指定要生成API文檔的swagger json文件進行轉化,如下
    npx redoc-cli bundle -o index.html swagger.json
  4. 然後通過nginx建立一個server直接指向該index.html文檔即可。配置示例:
server {
    listen 8081 default_server backlog=20480;
    listen [::]:8081 default_server;

    server_name localhost;

    access_log /home/ubuntu/logs/nginx/swagger-ui/swagger-ui.access.log main;
    error_log /home/ubuntu/logs/nginx/swagger-ui/swagger-ui.error.log;

    # 此處是存放index.html的目錄,作爲根目錄
    set $doc_root_dir "/home/ubuntu/swagger/swagger-ui-master/dist";

    location / {
        root $doc_root_dir;
        index index.php index.html index.htm;
     }
    # 此處用於緩存相關的文件
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 15d;
    }
    # 用於Amazon配置的健康檢查
    location /health_check {
        return 200;
    }
}

Swagger與Django

我們可以在Django中直接生成API文檔,但是需要兩個庫的支持,一個是DRF(Django Rest Framework),另一個是drf_yasg

  1. 安裝庫
pip install djangorestframework
pip install drf-yasg
  1. 在項目url文件中(根url文件)添加如下代碼
from django.conf.urls import include, url
from django.contrib import admin
from django.shortcuts import redirect
from rest_framework import permissions
from rest_framework.decorators import api_view

from drf_yasg import openapi
from drf_yasg.views import get_schema_view

swagger_info = openapi.Info(
    title="Snippets API",
    default_version='v1',
    description="""This is a demo project for the [drf-yasg](https://github.com/axnsan12/drf-yasg) Django Rest Framework library.

The `swagger-ui` view can be found [here](/cached/swagger).
The `ReDoc` view can be found [here](/cached/redoc).
The swagger YAML document can be found [here](/cached/swagger.yaml).

You can log in using the pre-existing `admin` user with password `passwordadmin`.""",  # noqa
    terms_of_service="https://www.google.com/policies/terms/",
    contact=openapi.Contact(email="[email protected]"),
    license=openapi.License(name="BSD License"),
)

SchemaView = get_schema_view(
    validators=['ssv', 'flex'],
    public=True,
    permission_classes=(permissions.AllowAny,),
)

urlpatterns = [
    url(r'^swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=0), name='schema-json'),
    url(r'^swagger/$', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
    url(r'^redoc/$', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
    url(r'^redoc-old/$', SchemaView.with_ui('redoc-old', cache_timeout=0), name='schema-redoc-old'),

    url(r'^cached/swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=None), name='cschema-json'),
    url(r'^cached/swagger/$', SchemaView.with_ui('swagger', cache_timeout=None), name='cschema-swagger-ui'),
    url(r'^cached/redoc/$', SchemaView.with_ui('redoc', cache_timeout=None), name='cschema-redoc'),
    ...
    
]
  1. Settings文件的設置
    install apps需要有如下app
INSTALLED_APPS = [
    ...
    'django.contrib.staticfiles',
    'rest_framework',
    'drf_yasg'
]

middleware中需要添加如下中間件

MIDDLEWARE = [
    ...
    'drf_yasg.middleware.SwaggerExceptionMiddleware',
]

其它相關設置

SWAGGER_SETTINGS = {
    'DEFAULT_INFO': 'testproj.urls.swagger_info',
}
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'testproj', 'static'),
]

  1. 在Serializer中定義相關fields時加上help_text就可以了
class AssetSerializer(serializers.Serializer):
    supportWithdraw = BooleanField(
        read_only=True, help_text='if support withdraw')
    supportDeposit = BooleanField(
        read_only=True, help_text='if support deposit')
    fee = FloatField(read_only=True, help_text='withdraw fee rate')
    currency = CharField(max_length=10, read_only=True, help_text='asset name')
    info = AssetInfoSerializer()

    def create(self, validated_data):
        pass

    def update(self, instance, validated_data):
        pass
  1. 啓動server後,可以通過訪問/swagger/ 或者/redoc/等路徑就可以看到對應的文檔

Swagger與Flask

生成Flask代碼

我們可以通過swagger-py-codegen來生成對應Flask的API代碼腳手架,過程如下:

  1. 安裝swagger-py-codegen
pip install swagger-py-codegen
  1. .yml文件拷貝到你的目錄中,這裏使用的是api.yml

  2. 進入該目錄,執行如下命令

swagger_py_codegen --swagger-doc api.yml example-app

這時就可以看到目錄中生成的Flask代碼,目錄結構如下:

example-app
├── example_app
│   ├── __init__.py
│   └── v1
│       ├── __init__.py
│       ├── api
│       │   ├── __init__.py
│       │   ├── pets.py
│       │   └── pets_petId.py
│       ├── routes.py
│       ├── schemas.py
│       └── validators.py
└── requirements.txt

swagger_py_codegen有如下選項:

Options:
  -s, --swagger-doc TEXT   Swagger doc file.  [required]
  -f, --force              Force overwrite.
  -p, --package TEXT       Package name / application name.
  -t, --template-dir TEXT  Path of your custom templates directory.
  --spec, --specification  Generate online specification json response.
  --ui                     Generate swagger ui.
  --validate               Validate swagger file.
  -tlp, --templates TEXT   gen flask/tornado/falcon/sanic templates, default
                           flask.
  --version                Show current version.
  --help                   Show this message and exit.

需要注意的是選項--spec--ui可以幫助我們生成在線的API文檔,使用示例:

swagger_py_codegen --swagger-doc api.yml example-app --ui --spec

這時,執行example_app/example_app下的init.py可以運行起Flask server,在瀏覽器中輸入URL"http://0.0.0.0:5000/static/swagger-ui/index.html#/default/get_users_uid"就可以看到生成的API文檔。

References

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章