Go言語を使ってRESTサーバを作るときの備忘録
今回使うライブラリはGin Web Framework。例にもれずGo言語にWebフレームワークには様々なものがあるが、今回は最も使われているGinを使います。
本記事は下記のような人向けです。
- Ginを使ってHTTPサーバーを始めたい
- Go言語を使ってHTTPサーバーをサクッと作りたい
- Go言語を使ってREST APIをサポートするバックエンドサーバーを作りたい
TOC
作業用ディレクトリの作成
今回作成するソースコード用のフォルダを作成します。
# 作業用ディレクトリの作成
mkdir restapi-srv
# Gitレポジトリのセットアップ
git init
touch README.md
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:eng-investor-admin/restapi-srv
git push -u origin main
# モジュールの作成とgo.modの初期化
go mod init github.com/eng-investor-admin
Ginのインストール
Githubのページに書いてあるとおりモジュールをインストールします。
参考 Gin Web Framework作業用ディレクトリ内でモジュールをインストール。
go get -u github.com/gin-gonic/gin
これにより、go.modファイルにginが追加されます。
Ginを使ってみる
さっそく、サンプルを使用して動かしてみます。
package main
import "github.com/gin-gonic/gin"
func main() {
router := gin.Default()
router.GET("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "pong",
})
})
router.Run()
}
このサンプルは大きく3つのパートに別れています。
ルータの作成
router := gin.Default()
これは、デフォルトの設定(loggerとrecovery)のミドルウェアを使用してルータを作成しています。
サーバの処理
router.GET("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "pong",
})
})
ルータに処理を追加するパートです。今回は/ping
に対してGETリクエストが来た場合にHTTPのステータス200OKを{"message": "pong"}
とともに返信するようにしています。
gin.Context
はginを扱う上で重要な部分で、受け取ったリクエストの内容や返答するためのコンテクストになります。gin.H
はmap[string]interface{}
への簡単な表現ctx.JSON
でJSONを返しています。
サーバーの起動
router.Run()
最後にこのHTTPサーバを起動しているパートです。
GETサンプルのテスト
実際に起動しましょう。
go run main.go
デフォルトだと8080番ポートを使用して起動します。
curlでアクセスして確認します。
$ curl -vvv http://localhost:8080/ping
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET /ping HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Sun, 26 Dec 2021 07:30:56 GMT
< Content-Length: 17
<
* Connection #0 to host localhost left intact
{"message":"pong"}
期待通りhttp://localhost:8080/ping
にGETリクエストを送ると{"message":"pong"}
が返ってきています。
POSTのデータを受け取る
先程のコードを拡張して、JSONのデータを受け取るようにします。
/user
というパスに対して{"user": "testuser1"}
のデータが来たとき、{"status": "testuser1"}
と返すようにします。
まず、送られてくるデータの構造体を作成します。
type userRequest struct {
User string `json:"user" binding:"required"`
}
JSONでどのようなキーを使っているか指示をするためにjson:"user"
、userを必須のパラメータとするため、binding:"required"
が指定されています。このように指定することで、ginのShouldBindJSON
で用意にデータの検証と取り出しを簡単に実施できます。
router.POST("/user", func(ctx *gin.Context) {
var userRequest userRequest
if err := ctx.ShouldBindJSON(&userRequest); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, gin.H{"status": userRequest.User})
})
続いて、POSTの処理を行う部分を上記のように追加します。
まず、受け取ったデータの検証と取り出しを行っています。ここでエラーが出た場合には、400 Bad Request
を返すようにしています。
リクエストが正当性が確認できた場合には、200 OK
と{"status": "ユーザ名"}
をデータして返答しています。
最後にmain.goは以下のとおりです。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
router := gin.Default()
router.GET("/ping", func(ctx *gin.Context) {
ctx.JSON(200, gin.H{
"message": "pong",
})
})
router.POST("/user", func(ctx *gin.Context) {
var userRequest userRequest
if err := ctx.ShouldBindJSON(&userRequest); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
ctx.JSON(http.StatusOK, gin.H{"status": userRequest.User})
})
router.Run()
}
type userRequest struct {
User string `json:"user" binding:"required"`
}
POSTのテスト
実際に起動してテストしてみましょう。
$ curl -vvv -X POST http://localhost:8080/user -d '{"user": "testuser1"}'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /user HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Length: 21
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 21 out of 21 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Content-Type: application/json; charset=utf-8
< Date: Sun, 26 Dec 2021 07:50:42 GMT
< Content-Length: 22
<
* Connection #0 to host localhost left intact
{"status":"testuser1"}
正しいリクエストを送ると200 OK
が返ってきています。
$ curl -vvv -X POST http://localhost:8080/user -d '{"use": "testuser1"}'
Note: Unnecessary use of -X or --request, POST is already inferred.
* Trying 127.0.0.1:8080...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /user HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.68.0
> Accept: */*
> Content-Length: 20
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 20 out of 20 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 400 Bad Request
< Content-Type: application/json; charset=utf-8
< Date: Sun, 26 Dec 2021 07:51:14 GMT
< Content-Length: 98
<
* Connection #0 to host localhost left intact
{"error":"Key: 'userRequest.User' Error:Field validation for 'User' failed on the 'required' tag"}
キーをuse
などの間違ったものにすると、400 Bad Request
とエラー内容が帰ってきています。
これによりJSONをデータとして扱う簡易的なRESTのサーバが作成できました。
さいごに
Ginを使うと簡単にGo言語でHTTPサーバを作成できます。データの検証や応答処理も簡単に記述できるので、リクエストを受け取った際のビジネスロジックに集中することが可能です。次回は、Ginを使ってOIDCのアクセストークンの検証を行います。