最も簡単なGoによるAPIサーバ
まずは
今回作成するのは、アクセスすると運勢を返すおみくじサーバ!
GET '/oracle'
すると、JSONでステータスコードと運勢を返すものを作っていきます。
"net/http"を用いてサーバを立てる
実際に、サーバを立てていきたいと思います。
net/http
パッケージを用いると簡単にサーバを立てることができます。
以下をmainに書くことで、localhost:8080
にhttpサーバが立つのです!とても簡単。
http.ListenAndServe(":8080", nil)
そして、"/"
にアクセスした時に何かが表示されるようにハンドラを追加しましょう。
func rootHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Oracle</h1>")
}
func main() {
http.HandleFunc("/", rootHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
このhttp.HandleFunc()
は第一引数にパス、第二引数にハンドラをとります
この関数は、DefaultServeMux
に、第一引数に与えられたパターンのためのhttpハンドラを登録します。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
このhttpハンドラとは、第一引数にResponseWriter
、第二引数に*Request
をとるServeHTTPがinterfaceでパッケージ内に定義されています。
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
つまり、ハンドラ(e.g. rootHandler
)を定義式のような形式で実装することで、Handlerタイプとなり、http.hundleFunc()
の第二引数となることができるのです。
(ここでは一旦、ServeHTTP
が何者かはそっと横に置いておきましょう。)
func rootHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Oracle</h1>")
}
とりあえずこれで、go run fileName
してあげると localhost:8080
にアクセスできると思います。
アクセスすると、h1タグの見た目のOracleという文字列が表示されるはずです!!
###JSONを返そう
では、本題。
JSONを返すようなハンドラを実装しましょう。
今回はJSONが返ってきたかを確認するために、一旦ブラウザで表示するようにしたいと思います。
まずはデータをstruct
で定義します。(Cをご存知の方はお馴染みのstructです)
type ResponseData struct {
Status int `json:"status"`
Data string `json:"data"`
}
今回ハマった点が一箇所ありました。
Goはstruct
等の変数の先頭文字を大文字にすると、他のパッケージからアクセスできて、小文字にすると、パッケージ内からのアクセスを許容する、という仕様があります。
最初アンダーケースでstruct
内の変数を書いていたら画面に表示されるのは空のデータ。
パッケージ内でも、出力する時はアッパーケースで書かないといけない模様。
長くなりました。
次に、httpハンドラの実装です。"/"
の時と同様にハンドラを登録します。
また、今回はおみくじなので乱数を必要とします。
故に、main関数が実行される度に、seedを設定しております。
func main() {
rand.Seed(time.Now().UnixNano())
http.HandleFunc("/oracle", oracleHandler)
http.HandleFunc("/", rootHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
では、oracleHandler
の中身を見ていきましょう。
まずは、おみくじの種類を格納する文字列の配列を定義します。
ここでconst
使おう、とか色々あると思いますが最も簡単に実装しました。
そして、変数responseをResponseDate
型で宣言し、ステータスコード200と先ほどの配列に乱数の(0~6)を代入して得られる運勢を格納し、初期化します。
"encoding/json"
パッケージを用いて、responseをJSONにエンコードします。
この時、 json.Marshal
は返り値として、エンコードされたデータとエラーを返します。
一応、このエラーを使って簡易な例外処理。
"net/http"
は優れもので、
http.StatusOK === 200
http.StatusInternalServerError === 500
みたいに、HTTPステータスコードをわかりやすく使えます。
そして最後に、引数のw http.ResponseWriter
を使って、
HTTPレスポンスのHeaderにJSONで返す胸を書いて、文字列にキャストされたデータをレスポンスに書いてやります。
ここで、文字列でキャストしているのは、json.Marshal
はbytes型でデータを扱っているためです。
func oracleHandler(w http.ResponseWriter, r *http.Request) {
// "/oracle"
oracles := []string{"大吉", "中吉", "小吉", "末吉", "吉", "凶", "大凶"}
response := ResponseData{http.StatusOK, oracles[rand.Intn(7)]}
res, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(res))
}
これにて、完成です。
実際にAPIにリクエストを投げてみましょう。
このような形で、レスポンスが返ってくるのではないでしょうか。
最後にソースコードを載せておきます。
ソースコード
import (
"encoding/json"
"fmt"
"log"
"math/rand"
"net/http"
"time"
)
type ResponseData struct {
Status int `json:"status"`
Data string `json:"data"`
}
func oracleHandler(w http.ResponseWriter, r *http.Request) {
// "/oracle"
oracles := []string{"大吉", "中吉", "小吉", "末吉", "吉", "凶", "大凶"}
response := ResponseData{http.StatusOK, oracles[rand.Intn(7)]}
res, err := json.Marshal(response)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, string(res))
}
func rootHandler(w http.ResponseWriter, r *http.Request) {
// "/"
fmt.Fprintf(w, "<h1>Oracle</h1>")
}
func main() {
rand.Seed(time.Now().UnixNano())
http.HandleFunc("/oracle", oracleHandler)
http.HandleFunc("/", rootHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
最後に
最後まで見ていただいた方、拙い記事で申し訳ありませんが、誠にありがとうございます。
初めての記事ですので、これを元に少しずつより良いものにしていこうと思います!!