最も簡単なGoによるAPIサーバ

# 最も簡単なGoによるAPIサーバ

### まずは
今回作成するのは、アクセスすると運勢を返すおみくじサーバ！
`GET　'/oracle'` すると、JSONでステータスコードと運勢を返すものを作っていきます。

### "net/http"を用いてサーバを立てる
実際に、サーバを立てていきたいと思います。
`net/http` パッケージを用いると簡単にサーバを立てることができます。

以下をmainに書くことで、`localhost:8080` にhttpサーバが立つのです！とても簡単。
`http.ListenAndServe(":8080", nil)`
　　
そして、`"/"` にアクセスした時に何かが表示されるようにハンドラを追加しましょう。

```go
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ハンドラを登録します。

```go
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}
```

このhttpハンドラとは、第一引数に`ResponseWriter`、第二引数に`*Request`をとるServeHTTPがinterfaceでパッケージ内に定義されています。

```go
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
```

つまり、ハンドラ（e.g. `rootHandler`)を定義式のような形式で実装することで、Handlerタイプとなり、`http.hundleFunc()`の第二引数となることができるのです。
（ここでは一旦、`ServeHTTP` が何者かはそっと横に置いておきましょう。）

```go
func rootHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "<h1>Oracle</h1>")
}
```

とりあえずこれで、`go run fileName` してあげると　`localhost:8080`　にアクセスできると思います。
アクセスすると、h1タグの見た目のOracleという文字列が表示されるはずです！！
<img width="592" alt="スクリーンショット 2018-12-30 0.40.06.png" src="https://qiita-image-store.s3.amazonaws.com/0/327586/33a10039-2976-5211-bc55-9666126dcdf4.png">


###JSONを返そう
では、本題。
JSONを返すようなハンドラを実装しましょう。
今回はJSONが返ってきたかを確認するために、一旦ブラウザで表示するようにしたいと思います。
まずはデータを`struct`で定義します。（Cをご存知の方はお馴染みのstructです）

```go
type ResponseData struct {
	Status int    `json:"status"`
	Data   string `json:"data"`
}
```

今回ハマった点が一箇所ありました。
Goは`struct`等の変数の先頭文字を大文字にすると、他のパッケージからアクセスできて、小文字にすると、パッケージ内からのアクセスを許容する、という仕様があります。
最初アンダーケースで`struct`内の変数を書いていたら画面に表示されるのは空のデータ。
パッケージ内でも、出力する時はアッパーケースで書かないといけない模様。

長くなりました。
次に、httpハンドラの実装です。`"/"` の時と同様にハンドラを登録します。
また、今回はおみくじなので乱数を必要とします。
故に、main関数が実行される度に、seedを設定しております。

```go
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型でデータを扱っているためです。

```go
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にリクエストを投げてみましょう。
このような形で、レスポンスが返ってくるのではないでしょうか。
<img width="616" alt="スクリーンショット 2018-12-30 0.28.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/327586/5d075b50-d8a1-4b6b-0880-40903537e262.png">




最後にソースコードを載せておきます。


### ソースコード
```go
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))
}
```

### 最後に
最後まで見ていただいた方、拙い記事で申し訳ありませんが、誠にありがとうございます。
初めての記事ですので、これを元に少しずつより良いものにしていこうと思います！！