現在お仕事を頂いているプロジェクトでは
Go Lang で API を製造している
しかし、私は Go Lang は素人あり、なにもわかっていないので、
「完全に理解した」状態に持っていくために
公式チュートリアルから、Restful API を行うためのチュートリアルをなぞったので、
自分なりの解釈を追加したメモ
公式ドキュメント
この記事は基本的に公式 Tutorial をなぞっていくだけ
前提条件
- Go 1.16 以降がインストールされていること 過去に入れた Go の version は 1.17.6
$ go version go version go1.17.6 windows/amd64
Go は入ってなければインストール
- editor が入っていること
→ VSCode を使用 - A command terminalg が使えること
→ Widows 環境でのチュートリアルを行うので CMD と bushを使用 - The curl tool
→ Curl を使用
>curl -V curl 7.79.1 (Windows) libcurl/7.79.1 Schannel Release-Date: 2021-09-22
API エンドポイントの仕様
チュートリアル記載の仕様
エンドポイント | HTTP メソッド | 概要 |
---|---|---|
/albums | Get | アルバムリストを取得して、Json を返す |
/albums | Post | Json リクエストを受けて、新規アルバムを追加する |
/albums/:id | Get | id に紐づくアルバムリストを取得して、Json を返す |
プロジェクトの作成
モジュールの作成
CMD を開いて、チュートリアル用フォルダを作成するディレクトリに移動
$ mkdir web-service-gin $ cd web-service-gin
モジュールの作成
$ go mod init example/web-service-gin go: creating new go.mod: module example/web-service-gin
ディレクトリ直下にgo.mod
が作成される
go.mod
https://go.dev/doc/modules/gomod-ref
データ作成
チュートリアルで使用するデータの作成
mian.go
ファイルを作成して編集
package main
記述
package main
※基本的には、go ファイルにはpackage main
が必要とのこと
- 構造体の宣言
type album struct { ID string `json:"id"` Title string `json:"title"` Artist string `json:"artist"` Price float64 `json:"price"` }
- 取得データの中身を定義
var albums = []album{ {ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99}, {ID: "2", Title: "Jeru", Artist: "Gerry Mulligan", Price: 17.99}, {ID: "3", Title: "Sarah Vaughan and Clifford Brown", Artist: "Sarah Vaughan", Price: 39.99}, }
ここまでが事前準備
API の作成
getAlbums
ここからは公式 Tutorial と順番を変えてやっていく
(順番通りにやっていくと linter に怒られるので)
1.import する package をpackage main
の下に追加
package main import ( "net/http" "github.com/gin-gonic/gin" )
2.依存関係の追加
コマンドラインで下記実行
$ go get .
成功すればgo get: added github.com/gin-gonic/gin v1.7.2
のような応答があるはず
3.データを取得する関数の実装
func getAlbums(c *gin.Context) {
c.IndentedJSON(http.StatusOK, albums)
}
4.エンドポイント の実装
func main() { router := gin.Default() router.GET("/albums", getAlbums) router.Run("localhost:8080") }
http://localhost:8080/albums
をたたくと
Get メソッドにより先ほど実装したgetAlbums
を返すエンドポイントを実装
※ここでは、関数ではなく関数名をわたす必要があるとのこと
5.動作確認 go go ファイルがあるディレクトリで下記実行
$ go run .
さらにコマンドラインより curl コマンド API をたたく
curl http://localhost:8080/albums
成功すると下記のような json が返却される
$ curl http://localhost:8080/albums [ { "id": "1", "title": "Blue Train", "artist": "John Coltrane", "price": 56.99 }, { "id": "2", "title": "Jeru", "artist": "Gerry Mulligan", "price": 17.99 }, { "id": "3", "title": "Sarah Vaughan and Clifford Brown", "artist": "Sarah Vaughan", "price": 39.99 } ]
go 側のレスポンス
[GIN] 2022/01/28 - 23:31:00 | 200 | 0s | 127.0.0.1 | GET "/albums"
200 ともに、データすべてが json とともに取得できることを確認できた
実装メモ
c.IndentedJSON(http.StatusOK, albums>
部分で、albums すべてを json としてhttp.StatusOK
を添えて返す関数を実装した
postAlbums
ここからは簡単に書いていく
1.post プロセスの実装
func postAlbums(c * gin.Context) { var newAlbum album if err := c.BindJSON(&newAlbum); err != nil { return } albums = append(albums,newAlbum) c.IndentedJSON((http.StatusCreated, newAlnewAlbum)) }
2.エンドポイントの追加
func main() { router := gin.Default() router.GET("/albums", getAlbums) + router.POST("/albums", postAlbums) router.Run("localhost:8080") }
3.API をたたく
公式記載の curl コマンドは CMD からだろ Bad Request が返ってくるので、
(おそらく書き方がおかしい)
git bush から実行
curl http://localhost:8080/albums \ --include \ --header "Content-Type: application/json" \ --request "POST" \ --data '{"id": "4","title": "The Modern Sound of Betty Carter","artist": "Betty Carter","price": 49.99}'
レスポンス
HTTP/1.1 201 Created Content-Type: application/json; charset=utf-8 Date: Fri, 28 Jan 2022 22:30:03 GMT Content-Length: 116 { "id": "4", "title": "The Modern Sound of Betty Carter", "artist": "Betty Carter", "price": 49.99 }
この状態で、Get リクエストを送ると、メモリ内にデータが追加されたのが確認できる
実装メモ
var newAlbum album
で、album 構造体をもつ newAlbum を実装
if err := c.BindJSON(&newAlbum); err != nil { return }
で、リクエスト json を構造体にバインド
失敗するとerr
に 400 が格納されるので、
err != nill
に引っかかってそのまま return する
バインドに成功した場合は
albums = append(albums,newAlbum)
より、newAlbum に格納されている値を albums に追加する
getAlbumByID
最後にパラメータを持たせた API の実装を行う
1.実装
func getAlbumByID( c * gin.Context) { id := c.Param("id") for _, a := range albums { if a.ID == id { c.IndentedJSON(http.StatusOK, a) return } } c.IndentedJSON(http.StatusNotFound, gin.H{"message":"album not found"}) }
2.エンドポイントの追加
func main() { router := gin.Default() router.GET("/albums", getAlbums) + router.GET("/albums/:id", getAlbumByID) router.POST("/albums", postAlbums) router.Run("localhost:8080") }
3.API をたたく
http://localhost:8080/albums/2
レスポンス
{ "id": "2", "title": "Jeru", "artist": "Gerry Mulligan", "price": 17.99 }
ID : 2
のデータが取得できる
ちなみに、存在しない ID を指定すると
>curl http://localhost:8080/albums/5 { "message": "album not found" }
message が返却される
実装メモ
id := c.Param("id")
でリクエストが持っているパラメータを取得
for _, a := range albums { if a.ID == id { c.IndentedJSON(http.StatusOK, a) return } }
albums の要素を for ループで回して ID が一致するものがあれば、json として返す
また、ID がヒットしない場合は
c.IndentedJSON(http.StatusNotFound, gin.H{"message":"album not found"})
より、hedder に message を付与して返す