46
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

LOBAdvent Calendar 2018

Day 23

go-swagger を使って swagger から Goのサーバとクライアントのコードを生成する

Last updated at Posted at 2018-12-23

swaggerの定義ファイルからgo-swaggerを使ってGoで書かれたサーバとクライアントのコードを生成して,実際にAPIを叩いてみようと思います

公式ドキュメントはこちら

swagger とは

swagger とは RESTful API の仕様を定義したオープンソースのフレームワークです
マイクロソフト, Google, IBM などが参加する 「OPEN API Initiative」 という団体が The Linux Foundation の協力のもと推進しています
サーバ, クライアントのコード以外にもテストやドキュメントの生成もすることができます

インストール

install from source

go get -u github.com/go-swagger/go-swagger/cmd/swagger

install from brew

brew install go-swagger

準備

GOPATH配下に swagger.yaml を準備します

mkdir $GOPATH/src/swagger
cd $GOPATH/src/swagger
swagger: "2.0"
info:
  version: "1.0.0"
  title: "Swagger lob"
  description: "A sample for LOB API"
host: "localhost:8000"
basePath: "/api"
schemes:
  - "http"
consumes:
  - "application/json"
produces:
  - "application/json"
paths:
  /users:
    post:
      operationId: postUser
      parameters:
      - in: body
        name: body
        required: true
        schema:
          $ref: '#/definitions/User'
      responses:
        '201':
          description: Created
  /users/{id}:
    get:
      operationId: getUserById
      parameters:
      - name: "id"
        in: "path"
        required: true
        type: "string"
      responses:
        '200':
          description: Success
          schema:
            $ref: '#/definitions/User'
definitions:
  User:
    type: object
    properties:
      id:
        type: number
        format: uint
      name:
        type: string

POST /api/users で user を作成し
GET /api/users/{id} で指定したidのユーザをゲットするシンプルな swagger.yaml です

サーバサイドのコードの生成

swagger generate server -f swagger.yaml

↑を叩くと以下のようにサーバ側のコードが生成されます

.
├── cmd
│   └── swagger-lob-server
│       └── main.go
├── models
│   └── user.go
├── restapi
│   ├── configure_swagger_lob.go
│   ├── doc.go
│   ├── embedded_spec.go
│   ├── operations
│   │   ├── get_user_by_id.go
│   │   ├── get_user_by_id_parameters.go
│   │   ├── get_user_by_id_responses.go
│   │   ├── get_user_by_id_urlbuilder.go
│   │   ├── post_user.go
│   │   ├── post_user_parameters.go
│   │   ├── post_user_responses.go
│   │   ├── post_user_urlbuilder.go
│   │   └── swagger_lob_api.go
│   └── server.go
└── swagger.yaml

models には definitions で定義された user モデルが定義され
retapi/operations/ にはリクエスト時のパラメータ等を持ったstructや、レスポンス用のstructが定義されています

生成がうまくいかないときは swagger に誤りがある可能性がありますので
バリデーションをかけて確認しましょう

swagger validate swagger.yaml
The swagger spec at "swagger.yaml" is valid against swagger specification 2.0

動かしてみる

この状態で一度動かしてみます
port を指定しないと毎回ランダムで起動してしまいます

go run cmd/swagger-lob-server/main.go --port=8000
curl http://127.0.0.1:8000/api/users/1
"operation .GetUsersID has not yet been implemented"

まだこの時点では、APIの中身を実装していないので、このようなレスポンスが返ってきます

実装

APIの中身を実装していきます

restapi/handler を作成し、その中にhandlerを作っていきます

.
├── cmd
├── models
├── restapi
│   ├── handler
│   │   ├── get_user_by_id.go
│   │   └── post_user.go
│   ├── operations
│   └── server.go
└── swagger.yaml

POST /user で handler のグローバルな変数にユーザを詰め
GET /user/{id} でユーザを取得しレスポンスに詰めて返しています

post_user.go
package handler

import (
	"github.com/go-openapi/runtime/middleware"
	"swagger/models"
	"swagger/restapi/operations"
)

var (
	Users map[uint64]models.User
)

func init()  {
	Users = map[uint64]models.User{}
}

type PostUserHandler struct {}

func (h *PostUserHandler) Handle(params operations.PostUserParams) middleware.Responder {
	u := models.User{
		ID:params.Body.ID,
		Name: params.Body.Name,
	}

	Users[params.Body.ID] = u

	return operations.NewPostUserCreated()
}
get_user_by_id.go
package handler

import (
	"github.com/go-openapi/runtime/middleware"
	"github.com/golang/go/src/pkg/strconv"
	"swagger/restapi/operations"
)

type GetUserByIDHandler struct {}

func (h *GetUserByIDHandler) Handle(params operations.GetUserByIDParams) middleware.Responder {
	id, _ := strconv.ParseUint(params.ID, 10, 64)

	u := Users[id]

	return operations.NewGetUserByIDOK().WithPayload(&u)
}

最後に configure_swagger_lob.go に handler を登録します
go-swagger から生成された時点では以下のようになっているので

configure_swagger_lob.go
api.GetUserByIDHandler = operations.GetUserByIDHandlerFunc(func(params operations.GetUserByIDParams) middleware.Responder {
		return middleware.NotImplemented("operation .GetUserByID has not yet been implemented")
	})
	api.PostUserHandler = operations.PostUserHandlerFunc(func(params operations.PostUserParams) middleware.Responder {
		return middleware.NotImplemented("operation .PostUser has not yet been implemented")
	})

以下のように更新します

configure_swagger_lob.go
api.GetUserByIDHandler = operations.GetUserByIDHandlerFunc(func(params operations.GetUserByIDParams) middleware.Responder {
		h := handler.GetUserByIDHandler{}
		return h.Handle(params)
	})
	api.PostUserHandler = operations.PostUserHandlerFunc(func(params operations.PostUserParams) middleware.Responder {
		h := handler.PostUserHandler{}
		return h.Handle(params)
	})

クライアントサイドのコード生成

swagger generate client -f swagger.yaml

↑を実行すると以下のコードが生成されます

.
├── client
│   ├── operations
│   │   ├── get_user_by_id_parameters.go
│   │   ├── get_user_by_id_responses.go
│   │   ├── operations_client.go
│   │   ├── post_user_parameters.go
│   │   └── post_user_responses.go
│   └── swagger_lob_client.go
└── swagger.yaml

ここでもリクエストを投げるのに必要なパラメータ等のstructが生成されますので、これらを利用したコードを書きます

post_user.go
package main

import (
	"log"
	"time"
	"swagger/client/operations"
	"swagger/models"
	apiclient "swagger/client"
	httptransport "github.com/go-openapi/runtime/client"
	"github.com/go-openapi/strfmt"
)

func main() {
	p := &operations.PostUserParams{
		Body: &models.User{
			ID: 1,
			Name: "name_lob",
		},
	}
	p.SetTimeout(10 * time.Second)

	transport := httptransport.New("localhost:8000", "api", nil)
	client := apiclient.New(transport,strfmt.Default)

	res, _ := client.Operations.PostUser(p)

	log.Printf("%#v\n", res.Error())
}
get_uesr_by_id.go
package main

import (
	"log"
	"time"
	"swagger/client/operations"
	apiclient"swagger/client"
	httptransport "github.com/go-openapi/runtime/client"
	"github.com/go-openapi/strfmt"
)

func main()  {
	p := operations.GetUserByIDParams{
		ID: "1",
	}
	p.SetTimeout(10 * time.Second)

	transport := httptransport.New("localhost:8000", "api", nil)
	client := apiclient.New(transport,strfmt.Default)

	res, _ := client.Operations.GetUserByID(&p)

	log.Printf("%+v\n", res)
}

p.SetTimeout(10 * time.Second)
を指定しないと,タイムアウトになってしまったのでここでは10秒で指定しています

ここでは,パラメータ値が固定ですがCLIツールとして,叩くときに引数でわたしてやれば便利に使えそうです

最後に

以上swaggerからgo-swaggerを使って,Goのサーバ,クライアントのコードを生成してAPIを叩いてみるまでを実装してみました
今回は触れていませんが、swaggerではテストやドキュメントの生成等も可能です

実際の開発の現場でも,APIの実装が終了するまではクライアントサイドにswaggerから生成したモックを叩いてもらう.というような使い方もできると思います

46
39
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?