出品API
本文を読む
ここまでは読み取りだけでした。今度は送られてきたデータを受け取ります。
Go
func receiveEvent(w http.ResponseWriter, r *http.Request) {
var event Event
err := json.NewDecoder(r.Body).Decode(&event)
if err != nil {
w.WriteHeader(400)
return
}
}Decode は、届いた JSON を構造体に流し込みます。Marshal の逆向きです。ここでは予定を受け取る別の題材で示しています。
&event の & は何か
Go
Decode(&event) // 居場所を渡す
Decode(event) // これでは書き込めない第6章のポインタレシーバと同じ話です。Decode は event の中身を書き換えるので、コピーではなく居場所を渡す必要があります。
& を書き忘れると、Go が教えてくれます。
json: Unmarshal(non-pointer main.Event)知らないキーは無視される
JSON
{"name":"きのこ図鑑","price":1200,"unknown":"何か"}unknown に対応するフィールドが無くても、エラーにはなりません。黙って捨てられます。
逆に、送られてこなかったフィールドはゼロ値のままです。
JSON
{"name":"きのこ図鑑"}Price は 0 になります。第2章のゼロ値がここでも効いてきます。
「0円で送ってきた」と「price を送ってこなかった」は区別できません。 どちらも 0 です。だから、受け取ったあとに検証が要ります。
検証してから受け付ける
第4章と第6章で書いた検証が、そのまま使えます。次は予定の入力を検証する例です。
Go
if err := validateEvent(event); err != nil {
w.WriteHeader(400)
w.Write([]byte(fmt.Sprintf(`{"error":%q}`, err.Error())))
return
}if err := 呼び出し; err != nil は、結果を受け取ってすぐ条件を調べる書き方です。この err はその if ブロックの中だけで使えます。err.Error() はエラーの説明を文字列で返します。
%q は文字列をダブルクォートで囲みます。JSON の整形は演習の writeError に任せます。この補助関数は json.Marshal を使い、400と本文を返します。呼び出しただけではハンドラは終わらないので、その後の return が必要です。
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)で本文を読む- 読み取りに失敗したら
writeErrorに「不正なJSONです」を渡して return する - 読み取り成功後に
validate(item)を呼び、失敗したら理由を渡して return する - 検証成功時は用意済みの201応答へ進む