Open4

go で API サーバを作る

yocyoc

完成形

package main

import (
	"fmt"
	"net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("hello world")
}

func main(){
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}

説明

(w http.ResponseWriter, r *http.Request) を受け取る関数を作成し HandleFunc に設定する。
この作成した関数がリクエストパスごとに処理を呼び出す。

定義ができたら ListenAndServe でサーバを起動する

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Println("hello world")
}

func main(){
	http.HandleFunc("/", handler)
	http.ListenAndServe(":8080", nil)
}
yocyoc

Jsonを返却したい場合

返却したいJsonの構造体を作っておく

type User struct {
	Id   string `json:"id"`
	Name string `json:"name"`
}

処理としては2つ

  1. 構造体をJsonにマーシャルする
  2. Json を ResponseWriter のbodyに書き込む
func handler(w http.ResponseWriter, r *http.Request) {
	// レスポンス用の構造体を作成
	user := User{
		Id:   "1",
		Name: "AAA",
	}
	// Jsonに変換
	_json, err := json.Marshal(user)
	// 変換に失敗したらエラーとする
	if err != nil {
		http.Error(w, err.Error(), 500)
		return
	}

	// 変換したJsonをbodyに書き込む
	w.WriteHeader(http.StatusOK)
	w.Write(_json)
}

yocyoc

ここまでは標準ライブラリである net/http しか使ってなかった。
下記のようにリクエストパスに任意の値を受け取って、その値を取り回すといったことをするにはひと手間かかる

http.HandleFunc("/{ID}/name", handler)

やりたい場合、URLを切り取ってなんとかするしかない

userId := strings.TrimSuffix(r.URL.Path, "/name")
yocyoc

gorilla/mux

ルーティングだけをライブラリ使いたくなったので gorilla/mux を使う
gorilla/mux

機能については少し古いけど下記記事に簡単な使い方がまとまっている
https://qiita.com/gold-kou/items/99507d33b8f8ddd96e3a

リクエストパスの値を使う方法

// gorilla/muxのRouterインスタンスを生成
r := mux.NewRouter()
r.HandleFunc("/{ID}/name", handler) 

func handler(w http.ResponseWriter, r *http.Request) { 
    // ライブラリの関数を使うと取得可能
    vars := mux.Vars(r) 
    id := vars["ID"]
}

クエリパラメータを設定する方法

r := mux.NewRouter()
// /name?id=xxx だった場合handlerが呼ばれる
r.HandleFunc("/name", handler) .Queries("id", "{id}")

ハマったこと

複数のクエリパラメータを使う場合、それぞれでQueriesを定義する必要がある
/name?id=xxx&age=yyy というリクエストと /name?id=xxx を両方使われる場合

// NG
r := mux.NewRouter()
// /name?id=xxx だった場合handlerが呼ばれる
r.HandleFunc("/name", handler) .Queries("id", "{id}").Queries("age", "{age}")

このように定義しておけば両方のパターンに対応できそうだけどできないみたい。
Queries で定義した組み合わせのみを条件としてhandlerが呼ばれるみたい。

// OK
r := mux.NewRouter()
// /name?id=xxx だった場合handlerが呼ばれる
r.HandleFunc("/name", handler) .Queries("id", "{id}").Queries("age", "{age}")
r.HandleFunc("/name", handler) .Queries("id", "{id}")

これで /name?id=xxx&age=yyy と /name?id=xxx というリクエストを両方handlerへ流すことができるようになる