使用go-swagger為golang API自動生成swagger文件

Poisoner發表於2018-05-23

什麼是swagger?

Swagger是一個簡單但功能強大的API表達工具。它具有地球上最大的API工具生態系統,數以千計的開發人員,使用幾乎所有的現代程式語言,都在支援和使用Swagger。使用Swagger生成API,我們可以得到互動式文件,自動生成程式碼的SDK以及API的發現特性等。

swagger文件長啥樣?

一個最簡單的swagger文件示例:

swagger: "2.0"

info:
  version: 1.0.0
  title: Simple API
  description: A simple API to learn how to write OpenAPI Specification

schemes:
  - https
host: simple.api
basePath: /openapi101

paths: {}
複製程式碼

Tips:閱讀本文前提是假設你已經瞭解瞭如何編寫swagger文件,當然,如果還不瞭解也沒關係,可以去swagger官網檢視文件進行學習,並且這裡還有一套《Swagger從入門到精通》附上.

本文背景介紹

​ 寫作本文的原因是因為公司要求api文件都使用 swagger格式,專案是用golang編寫的,作為一個懶癌程式設計師,怎麼能夠忍受去編寫這麼複雜的swagger文件呢?有沒有一鍵生成的工具呢?google一下,還真有,那就是go-swagger專案。go-swagger眾多特色功能之一就是Generate a spec from source,即通過原始碼生成文件,很符合我的需求。

下面就簡單介紹下如何為專案加上swagger註釋,然後一鍵生成API文件

開始之前需要安裝兩個工具:

  • swagger-editor:用於編寫swagger文件,UI展示,生成程式碼等...
  • go-swagger:用於一鍵生成API文件

安裝swagger-editor,我這裡使用docker執行,其他安裝方式,請檢視官方文件

docker pull swaggerapi/swagger-editor
docker run --rm -p 80:8080 swaggerapi/swagger-editor
複製程式碼

安裝go-swagger,我這邊使用brew安裝,其他安裝方式,請檢視官方文件

brew tap go-swagger/go-swagger
brew install go-swagger
複製程式碼

好了,現在終於開始正題:start coding!!!

開始編寫註釋

1.假設有一個user.server,提供一些REST API,用於對使用者資料的增刪改查。

比如這裡有一個getOneUser介面,是查詢使用者資訊的:

package service

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
    "user.server/models"
    "github.com/Sirupsen/logrus"
)

type GetUserParam struct {
    Id int `json:"id"`
}

func GetOneUser(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close()
    decoder := json.NewDecoder(r.Body)
    var param GetUserParam
    err := decoder.Decode(&param)
    if err != nil {
        WriteResponse(w, ErrorResponseCode, "request param is invalid, please check!", nil)
        return
    }

    // get user from db
    user, err := models.GetOne(strconv.Itoa(param.Id))
    if err != nil {
        logrus.Warn(err)
        WriteResponse(w, ErrorResponseCode, "failed", nil)
        return
    }
    WriteResponse(w, SuccessResponseCode, "success", user)
}
複製程式碼

根據swagger文件規範,一個swagger文件首先要有swagger的版本和info資訊。利用go-swagger只需要在宣告package之前加上如下注釋即可:

// Package classification User API.
//
// The purpose of this service is to provide an application
// that is using plain go code to define an API
//
//      Host: localhost
//      Version: 0.0.1
//
// swagger:meta
package service
複製程式碼

然後在專案根目錄下使用swagger generate spec -o ./swagger.json命令生成swagger.json檔案:

此命令會找到main.go入口檔案,然後遍歷所有原始碼檔案,解析然後生成swagger.json檔案

{
  "swagger": "2.0",
  "info": {
    "description": "The purpose of this service is to provide an application\nthat is using plain go code to define an API",
    "title": "User API.",
    "version": "0.0.1"
  },
  "host": "localhost",
  "paths": {}
}
複製程式碼

2.基本資訊有了,然後就要有路由,請求,響應等,下面針對getOneUser介面編寫swagger註釋:

// swagger:parameters getSingleUser
type GetUserParam struct {
    // an id of user info
    //
    // Required: true
    // in: path
    Id int `json:"id"`
}

func GetOneUser(w http.ResponseWriter, r *http.Request) {
    // swagger:route GET /users/{id} users getSingleUser
    //
    // get a user by userID
    //
    // This will show a user info
    //
    //     Responses:
    //       200: UserResponse
    decoder := json.NewDecoder(r.Body)
    var param GetUserParam
    err := decoder.Decode(&param)
    if err != nil {
        WriteResponse(w, ErrorResponseCode, "request param is invalid, please check!", nil)
        return
    }

    // get user from db
    user, err := models.GetOne(strconv.Itoa(param.Id))
    if err != nil {
        logrus.Warn(err)
        WriteResponse(w, ErrorResponseCode, "failed", nil)
        return
    }
    WriteResponse(w, SuccessResponseCode, "success", user)
}
複製程式碼

可以看到在GetUserParam結構體上面加了一行swagger:parameters getSingleUser的註釋資訊,這是宣告介面的入參註釋,結構體內部的幾行註釋指明瞭id這個引數必填,並且查詢引數id是在url path中。詳細用法,參考: swagger:params

GetOneUser函式中:

  • swagger:route指明使用的http method,路由,以及標籤和operation id,詳細用法,參考: swagger:route
  • Responses指明瞭返回值的code以及型別

然後再宣告響應:

// User Info
//
// swagger:response UserResponse
type UserWapper struct {
    // in: body
    Body ResponseMessage
}

type ResponseMessage struct {
    Code    int         `json:"code"`
    Message string      `json:"message"`
    Data    interface{} `json:"data"`
}
複製程式碼

使用swagger:response語法宣告返回值,其上兩行是返回值的描述(我也不清楚,為啥描述資訊要寫在上面,歡迎解惑),詳細用法,參考; swagger:response

然後瀏覽器訪問localhost,檢視swagger-editor介面,點選工具欄中的File->Impoprt File上傳剛才生成的 swagger.json檔案,就可以看到介面:

使用go-swagger為golang API自動生成swagger文件

這樣一個簡單的api文件就生成了

3.怎麼樣?是不是很簡單?可是又感覺那裡不對,嗯,註釋都寫在程式碼裡了,很不美觀,而且不易維護。想一下go-swagger的原理是掃描目錄下的所有go檔案,解析註釋資訊。那麼是不是可以把api註釋都集中寫在單個檔案內,統一管理,免得分散在各個原始碼檔案內。

新建一個doc.go檔案,這裡還有一個介面是UpdateUser,那麼我們在doc.go檔案中宣告此介面的api註釋。先看一下UpdateUser介面的程式碼:

func UpdateUser(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close()
    // decode body data into user struct
    decoder := json.NewDecoder(r.Body)
    user := models.User{}
    err := decoder.Decode(&user)
    if err != nil {
        WriteResponse(w, ErrorResponseCode, "user data is invalid, please check!", nil)
        return
    }

    // check if user exists
    data, err := models.GetUserById(user.Id)
    if err != nil {
        logrus.Warn(err)
        WriteResponse(w, ErrorResponseCode, "query user failed", nil)
        return
    }
    if data.Id == 0 {
        WriteResponse(w, ErrorResponseCode, "user not exists, no need to update", nil)
        return
    }

    // update
    _, err = models.Update(user)
    if err != nil {
        WriteResponse(w, ErrorResponseCode, "update user data failed, please try again!", nil)
        return
    }
    WriteResponse(w, SuccessResponseCode, "update user data success!", nil)
}
複製程式碼

然後再doc.go檔案中編寫如下宣告:

package service

import "user.server/models"

// swagger:parameters UpdateUserResponseWrapper
type UpdateUserRequest struct {
    // in: body
    Body models.User
}

// Update User Info
//
// swagger:response UpdateUserResponseWrapper
type UpdateUserResponseWrapper struct {
    // in: body
    Body ResponseMessage
}

// swagger:route POST /users users UpdateUserResponseWrapper
//
// Update User
//
// This will update user info
//
//     Responses:
//       200: UpdateUserResponseWrapper
複製程式碼

這樣就把api宣告註釋給抽離出來了,然後使用命令swagger generate spec -o ./swagger.json生成json檔案,就可以看到這樣的結果:

使用go-swagger為golang API自動生成swagger文件

很簡單吧,參照文件編寫幾行註釋,然後一個命令生成API文件。懶癌程式設計師福音~

本文所有示例程式碼託管在這裡, 原文地址

參考:

相關文章