- はじめに
- APIの全体像
- MongoDBの作成
- Dockerfileの作成
- Dockerビルド
- Go-Serverの作成
- APIの実装
- CreateUserメソッドの実装
- GetUserByNameメソッドの実装
- go/api_user.goの全体
- Dockerfileの作成
- Dockerビルド
- APIの実装
- APIの確認
- docker-compose.yamlの作成
- コンテナの起動
- curlによるAPIの確認
はじめに
SwaggerEditorはOpenAPI specを様々な言語に変換することができます。以下の記事では、SwaggerEditorを使って簡単なOpenAPI specを作成し、Go-Serverに変換するところまで紹介しました。
MacにSwaggerEditorをインストールしてOpenAPI specを実装する
しかし、あくまでもAPIの雛形であって、中身の処理が実装されているわけではありません。本記事では、上記の記事で作成したGo-Serverに処理を実装します。

APIの全体像
本記事で作成するAPIの全体像について紹介します。
APIの仕様は以下の通りです。DBにはMongoDBを利用し、apiとdbはそれぞれコンテナで起動します。
- POST /v2/user/create・・・Context-TypeでJson形式のユーザー情報を受け付け、DBに保管します。
- GET /v2/user/{username}・・・URLに指定したusernameとマッチするレコードをDBから取得し、表示します。

MongoDBの作成
Dockerfileの作成
MongoDBのDockerfileを作成します。
ディレクトリ構造は以下の通りです。
$ tree mongo/ mongo/ ├── Dockerfile ├── init.sh └── users.json
今回は揮発性のDBコンテナにします。コンテナ起動時に初期データ(users.json)を流し込むinit.shも併せて作成します。
1. 初期データの作成
コンテナ起動時に流し込む初期データをjson形式で作成します。
[ { "username": "koratta", "email": "koratta@example.com" }, { "username": "koratta2", "email": "koratta2@example.com" } ]
2. 初期実行ファイルの作成
1で作成したJsonファイルをMongoDBに保管するためのスクリプトinit.shを作成します。
mongoimport --authenticationDatabase admin --username root --password password --db api --collection users --drop --file /docker-entrypoint-initdb.d/users.json --jsonArray
3. Dockerfileの作成
init.shをコンテナ起動時に実行するmongoベースのDockerfileを作成します。コンテナ起動時に実行するため、/docker-entrypoint-initdb.dにコピーします。
FROM mongo:latest COPY users.json /docker-entrypoint-initdb.d/ COPY init.sh /docker-entrypoint-initdb.d/
Dockerビルド
上記で作成したDockerfileをビルドし、コンテナイメージを作成します。
$ docker build -t api-mongo-db:latest mongo/
Go-Serverの作成
APIの実装
APIリクエストのルーティング情報は、go/routers.goに記述されています。
~~go/router.goより抜粋~~
var routes = Routes{ Route{ "Index", "GET", "/v2/", Index, }, Route{ "CreateUser", strings.ToUpper("Post"), "/v2/user/create", CreateUser, }, Route{ "GetUserByName", strings.ToUpper("Get"), "/v2/user/{username}", GetUserByName, }, }
/v2/user/createに対してPOSTされたらCreateUserメソッドを、/v2/user/{username}に対してGETされたらGetUserBynameメソッドを呼び出すようになっています。それぞれのメソッドはgo/api_user.goで定義されているので、それぞれを実装します。
CreateUserメソッドの実装
CreateUserメソッドを以下に示します。
func CreateUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") //ヘッダー情報の取得 if r.Header.Get("Content-Type") != "application/json" { w.WriteHeader(http.StatusBadRequest) return } length, err := strconv.Atoi(r.Header.Get("Content-Length")) body := make([]byte, length) length, err = r.Body.Read(body) var insert_record User err = json.Unmarshal(body[:length], &insert_record) //DBへのInsert connect := getConnect() _, err = connect.Database("api").Collection("users").InsertOne(context.Background(), insert_record) if err != nil { w.WriteHeader(http.StatusBadRequest) return } w.WriteHeader(http.StatusOK) }
9~11行目でPOSTされたJson形式のユーザー情報を取得しています。
length, err := strconv.Atoi(r.Header.Get("Content-Length")) body := make([]byte, length) length, err = r.Body.Read(body)
12~13行目でUser構造体のinsert_recordに変換しています。
var insert_record User err = json.Unmarshal(body[:length], &insert_record)
このUser構造体は、OpenAPI specから生成されていて、go/model_user.goに記述されています。
package swagger type User struct { Username string `json:"username,omitempty"` Email string `json:"email,omitempty"` }
16~17行目でMongoDBに保管しています。getConnectメソッドはMongoDBとの接続を確立するメソッドで、go/api_user.goの全体に載せています。
connect := getConnect() _, err = connect.Database("api").Collection("users").InsertOne(context.Background(), insert_record)
GetUserByNameメソッドの実装
GetUserByNameメソッドを以下に示します。
func GetUserByName(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") //検索ユーザー情報の取得 user := strings.Replace(r.URL.Path, "/v2/user/", "", 1) //user情報の取得 var output User connect := getConnect() filter := bson.D{{"username", user }} err := connect.Database("api").Collection("users").FindOne(context.Background(), filter).Decode(&output) if err != nil { w.WriteHeader(http.StatusBadRequest) return } //Jsonの出力 json.NewEncoder(w).Encode(output) w.WriteHeader(http.StatusOK) }
5行目で検索条件となるユーザー名を取得しています。今回のAPIの仕様は、URLのパスに指定されたユーザー名の情報を表示するので、URLパスからユーザー名を取得しています。
user := strings.Replace(r.URL.Path, "/v2/user/", "", 1)
9~11行目で、URLに指定されたユーザー名を基にMongoDBから情報を取得しています。
connect := getConnect() filter := bson.D{{"username", user }} err := connect.Database("api").Collection("users").FindOne(context.Background(), filter).Decode(&output)
go/user_api.goの全体
user_api.goの全体は以下の通りです。
/* * Koratta Test API * * This is sample api * * API version: 1.0.0 * Contact: koratta@example.com * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) */ package swagger import ( "net/http" "log" "time" "encoding/json" "context" "strconv" "strings" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" "go.mongodb.org/mongo-driver/bson" ) func CreateUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") //ヘッダー情報の取得 if r.Header.Get("Content-Type") != "application/json" { w.WriteHeader(http.StatusBadRequest) return } length, err := strconv.Atoi(r.Header.Get("Content-Length")) body := make([]byte, length) length, err = r.Body.Read(body) var insert_record User err = json.Unmarshal(body[:length], &insert_record) //DBへのInsert connect := getConnect() _, err = connect.Database("api").Collection("users").InsertOne(context.Background(), insert_record) if err != nil { w.WriteHeader(http.StatusBadRequest) return } w.WriteHeader(http.StatusOK) } func GetUserByName(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=UTF-8") //検索ユーザー情報の取得 user := strings.Replace(r.URL.Path, "/v2/user/", "", 1) //user情報の取得 var output User connect := getConnect() filter := bson.D{{"username", user }} err := connect.Database("api").Collection("users").FindOne(context.Background(), filter).Decode(&output) if err != nil { w.WriteHeader(http.StatusBadRequest) return } //Jsonの出力 json.NewEncoder(w).Encode(output) w.WriteHeader(http.StatusOK) } func getConnect() *mongo.Client{ //MongoDBの認証情報 credential := options.Credential{ Username: "root", Password: "password", } //MongoDBへの接続 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() connect, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://db:27017").SetAuth(credential)) if err != nil { log.Fatal(err) } return connect }
Dockerfileの作成
Dockerfileを作成します。
ディレクトリ構造は以下の通りです。
$ tree api api ├── Dockerfile └── src ├── api │ └── swagger.yaml ├── go │ ├── README.md │ ├── api_user.go │ ├── logger.go │ ├── model_user.go │ └── routers.go └── main.go
src配下のファイルは、MacにSwaggerEditorをインストールしてOpenAPI specを実装するで作成したソースコードです。
Dockerfileは以下の通りです。
FROM golang:latest # gouserの作成 RUN useradd -m -s /bin/bash gouser # コンテナ作業ディレクトリの変更 WORKDIR /go/src/api RUN chown gouser:gouser /go/src/api # モジュールのダウンロード RUN go get -u github.com/gorilla/mux &&\ go get -u "go.mongodb.org/mongo-driver/mongo" # ホストOSの ./src の中身を作業ディレクトリにコピー COPY --chown=gouser:gouser ./src . # go build RUN go build -o koratta-api &&\ chown gouser:gouser api # gouserに変更 USER gouser # API実行コマンドの実行 CMD ["/go/src/api/koratta-api"]
必要なモジュールのgorilla/muxとmongo-driver/mongoをインストールし、gouserに実行権限を与えてgouserで実行するようにしています。root権限でも当然実行できます。
注意点は、main.goのimportパッケージのsw "go"
部分です。コンテナの場合デフォルトで$GOPATH=/go/srcなので、上記のDockerfileの場合sw "api/go"
になります。
APIの確認
docker-compose.yamlの作成
MongoDBとAPIのコンテナを起動するためのdocker-compose.yamlを作成します。
version: '3' services: db: container_name: db image: api-mongo-db:latest environment: MONGO_INITDB_ROOT_USERNAME: root MONGO_INITDB_ROOT_PASSWORD: password ports: - "27017:27017" api: container_name: api image: api:latest depends_on: - db links: - db ports: - "8080:8080"
コンテナの起動
docker-composeを使ってコンテナを起動します。
$ docker-compose up -d
curlによるAPIの確認
作成したAPIの挙動を確認します。
初期データとして保管されているレコードをAPI経由で確認すると、以下の通り、指定したユーザー名に該当する情報を取得できます。
$ curl -X GET "http://localhost:8080/v2/user/koratta" {"username":"koratta","email":"koratta@example.com"}
保管されていないユーザーは、当然取得できません。
$ curl -X GET "http://localhost:8080/v2/user/koratta5 $
従って、APIを使ってユーザー情報を保管します。
$ curl -X POST "http://localhost:8080/v2/user/create" -H "accept: application/xml" -H "Content-Type: application/json" -d "{ \"username\": \"koratta5\", \"email\": \"koratta5@example.com\"}"
再度確認すると、ユーザー情報を確認することができます。/v2/user/createのAPI経由でDBに保管できていることが確認できました。
$ curl -X GET "http://localhost:8080/v2/user/koratta5" {"username":"koratta5","email":"koratta5@example.com"}
以上です。