8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-12-29

最も簡単な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という文字列が表示されるはずです!!
スクリーンショット 2018-12-30 0.40.06.png

###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にリクエストを投げてみましょう。
このような形で、レスポンスが返ってくるのではないでしょうか。
スクリーンショット 2018-12-30 0.28.42.png

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

ソースコード

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))
}

最後に

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

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?