GO/Gin gin-swagger使用してAPI仕様書を生成します


前書

この記事は gin-swaggerライブラリ使用してGinプロダクトにAPI仕様書生成するための知見をまとめるものになります。
gin-swaggerリポジトリリンク

テスト用のプロダクトを作成

フォルダを作成

// 任意のディレクトリで
$ mkdir use_swagger && cd use_swagger

go modを初期化

$ go mod init use_swagger

ライブラリインストール

$ go get -u github.com/gin-gonic/gin
$ go get -u github.com/swaggo/swag/cmd/swag
$ go get -u github.com/swaggo/gin-swagger
$ go get -u github.com/swaggo/files

インストール完了後のバージョン確認。

$ swag -v
swag version v1.6.7

現在のディレクトリ構成

|-- use_swagger
|-- |-- go.mod

プロダクトにAPI仕様書追加

use_swaggerディレクトリ配下にmain.goファイル作成

$ touch main.go

追加前

main.go
package main

import (
    "github.com/gin-gonic/gin"
)

func main() {
    r := gin.New()  
    r.GET("/test", test)    
    r.Run()
}

func test(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{ "msg": "ok"})
}

サーバー立ち上げ後 http://localhost:8080/testにアクセスすると、簡単なJsonデータが戻ってきます。

追加後

main.go
package main

import (
    "github.com/gin-gonic/gin"
    swaggerFiles "github.com/swaggo/files"
    "github.com/swaggo/gin-swagger"
    "net/http"

    _ "use_swagger/docs"
)

// @title APIドキュメントのタイトル
// @version バージョン(1.0)
// @description 仕様書に関する内容説明
// @termsOfService 仕様書使用する際の注意事項

// @contact.name APIサポーター
// @contact.url http://www.swagger.io/support
// @contact.email [email protected]

// @license.name ライセンス(必須)
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html

// @host localhost:8080
// @BasePath /
func main() {
    r := gin.New()

    url := ginSwagger.URL("http://localhost:8080/swagger/doc.json")
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url))

    r.GET("/test", test)
    r.Run()
}

// @description テスト用APIの詳細
// @version 1.0
// @accept application/x-json-stream
// @param none query string false "必須ではありません。"
// @Success 200 {object} gin.H {"code":200,"msg":"ok"}
// @router /test/ [get]
func test(c *gin.Context){
    c.JSON(http.StatusOK, gin.H{ "msg": "ok"})
}

仕様書を初期化します。

$ swag init

初期化完了後、ディレクトリ構成は以下のようになりました。

|-- use_swagger
|-- |-- docs
|-- |-- |-- docs.go
|-- |-- |-- swagger.json
|-- |-- |-- swagger.yaml
|-- |-- main.go
|-- |-- go.mod

サーバーを立ち上げ、http://localhost:8080/swagger/index.htmlへアクセスすれば、仕様書が表示されます。

実際使ってみます、適当にパラメータを渡して、実行します。

期待通りのレスポンスが返ってきました。

よく使用されるオプション

API オプション

オプション 説明
description 操作動作の詳細な説明。
id 操作を識別するために使用される一意の文字列。すべてのAPI操作の中で一意である必要があります。(使用されてる所あんまり見ない)
tags コンマで区切られた各API操作へのタグのリスト、APIの関連性を示します。
summary 操作の実行内容の簡単な要約。
accept APIが使用できるMIMEヘッダタイプのリスト。値は、MIMEヘッダタイプで説明されているとおりでなければなりません。
produce APIが生成できるMIMEタイプのリスト。値は、MIMEヘッダタイプで説明されているとおりでなければなりません。
param スペースで区切られたパラメーター。パラメータ名、パラメータタイプ、データタイプ、必須ですか?、コメント属性(オプション)
security 各API操作のセキュリティ。
success スペースで区切られた成功レスポンス。レスポンスコード、{param type}、データ型、コメント
failure スペースで区切られた障害レスポンス。レスポンスコード、{param type}、データ型、コメント
router パス、[httpMethod]

使用凡例

// @Summary Auth admin
// @Description get admin info
// @Tags accounts,admin
// @Accept  application/x-json-stream
// @Produce  application/x-json-stream
// @Success 200 {object} model.Admin
// @Failure 400 {object} httputil.HTTPError
// @Failure 401 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Security ApiKeyAuth
// @Router /admin/auth [post]
func (c *Controller) Auth(ctx *gin.Context) {
    authHeader := ctx.GetHeader("Authorization")
    if len(authHeader) == 0 {
        httputil.NewError(ctx, http.StatusBadRequest, errors.New("please set Header Authorization"))
        return
    }
    if authHeader != "admin" {
        httputil.NewError(ctx, http.StatusUnauthorized, fmt.Errorf("this user isn't authorized to operation key=%s expected=admin", authHeader))
        return
    }
    admin := model.Admin{
        ID:   1,
        Name: "admin",
    }
    ctx.JSON(http.StatusOK, admin)
}
// ShowBottle godoc
// @Summary Show a bottle
// @Description get string by ID
// @ID get-string-by-int
// @Tags bottles
// @Accept  json
// @Produce  json
// @Param  id path int true "Bottle ID"
// @Success 200 {object} model.Bottle
// @Failure 400 {object} httputil.HTTPError
// @Failure 404 {object} httputil.HTTPError
// @Failure 500 {object} httputil.HTTPError
// @Router /bottles/{id} [get]
func (c *Controller) ShowBottle(ctx *gin.Context) {
    id := ctx.Param("id")
    bid, err := strconv.Atoi(id)
    if err != nil {
        httputil.NewError(ctx, http.StatusBadRequest, err)
        return
    }
    bottle, err := model.BottleOne(bid)
    if err != nil {
        httputil.NewError(ctx, http.StatusNotFound, err)
        return
    }
    ctx.JSON(http.StatusOK, bottle)
}

その他API情報は公式ドキュメントこちらのリポジトリを参考にしてください。

使用感想

gin-swaggerライブラリは非常に便利とは言えない、コメントから生成されるAPI仕様書であるために、
記述する際には間違えがないかよく確認したほうがいいと思います。