出品API
本文を読む
ここまでは読み取りだけでした。今度は送られてきたデータを受け取ります。
Go
func createHandler(w http.ResponseWriter, r *http.Request) {
var item Item
err := json.NewDecoder(r.Body).Decode(&item)
if err != nil {
w.WriteHeader(400)
return
}
}Decode は、届いた JSON を構造体に流し込みます。Marshal の逆向きです。
&item の & は何か
Go
Decode(&item) // 居場所を渡す
Decode(item) // これでは書き込めない第6章のポインタレシーバと同じ話です。Decode は item の中身を書き換えるので、コピーではなく居場所を渡す必要があります。
& を書き忘れると、Go が教えてくれます。
プレーンテキスト
json: Unmarshal(non-pointer main.Item)知らないキーは無視される
JSON
{"name":"きのこ図鑑","price":1200,"unknown":"何か"}unknown に対応するフィールドが無くても、エラーにはなりません。黙って捨てられます。
逆に、送られてこなかったフィールドはゼロ値のままです。
JSON
{"name":"きのこ図鑑"}Price は 0 になります。第2章のゼロ値がここでも効いてきます。
「0円で送ってきた」と「price を送ってこなかった」は区別できません。 どちらも 0 です。だから、受け取ったあとに検証が要ります。
検証してから受け付ける
第4章と第6章で書いた検証が、そのまま使えます。
Go
if err := validate(item); err != nil {
w.WriteHeader(400)
w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
return
}%q は、文字列をダブルクォートで囲んで埋め込みます。JSON の値として書くときに便利です。
Go
fmt.Sprintf(`{"error":%q}`, "価格が0以下です")
// {"error":"価格が0以下です"}201 を返す
作成が成功したときは、200 ではなく 201 を使います。
プレーンテキスト
200 OK 取得できた
201 Created 新しく作った
400 Bad Request 送られてきた内容がおかしい201 は「作った」という意味です。200 でも動きますが、何が起きたのかが応答から読み取れるほうが親切です。
このあたりの使い分けは、決まりというより慣習です。チームで揃っていることのほうが、個々の選択より大切になります。
要件
json.NewDecoder(r.Body).Decode(&item)で本文を読む- 読み取りに失敗したら 400 と
{"error":"不正なJSONです"} validateに通し、エラーなら 400 とその理由- 成功したら 201 と受け取った商品の JSON
入出力例
callCreate("{"name":"きのこ図鑑","price":1200}") → "201 {"name":"きのこ図鑑","price":1200}"
callCreate("{"name":"","price":1200}") → "400 {"error":"商品名が空です"}"
callCreate("{"name":"本","price":0}") → "400 {"error":"価格が0以下です"}"
callCreate("{"name":"本"}") → "400 {"error":"価格が0以下です"}"
callCreate("{壊れている") → "400 {"error":"不正なJSONです"}"ヒント
編集 ゆめさく編集部