JSONレスポンスの書き込み
Go標準ライブラリのnet/httpパッケージを利用して、JSON形式のレスポンスを返却するREST APIの作成方法を説明します。 エラー処理としてエンコードエラーのハンドリング方法についても説明します。
基本
json
パッケージを利用することで、構造体のデータをJSONデータにエンコードできます。
次はHTTPレスポンスのボディにJSONデータを書き込むハンドラ関数です。
type Task struct {
ID string `json:"id"`
Title string `json:"title"`
Done bool `json:"done"`
Deleted bool `json:"-"`
}
func getTask(w http.ResponseWriter, r *http.Request) {
// ここにデータの取得処理があるとする
// データがなかった場合
if !found {
// JSONデータを書き込む
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
s := fmt.Sprintf("%d %s", code, http.StatusText(code))
m := map[string]string{"status": s}
if err := json.NewEncoder(w).Encode(m); err != nil {
log.Println(err)
}
return
}
t := Task{
ID: "1",
Title: "shopping",
}
// JSONデータを書き込む
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(t); err != nil {
log.Println(err)
}
}
エラー処理の強化
先ほどのコードでは、次の場合にエラーとなります。
- JSONのエンコードに失敗した場合
- レスポンスの書き込みに失敗した場合
前者の場合、ステータスコード500をレスポンスするには、次の通り処理します。 ポイントは、JSONのエンコード処理とレスポンスの書き込み処理を別にして、 エンコードのエラー処理をレスポンスの書き込み処理の前にすることです。
w.Header().Set("Content-Type", "application/json; charset=utf-8")
// JSONのエンコード
b, err := json.Marshal(v)
if err != nil {
log.Println(err)
// JSONエンコード失敗のため、エラーをレスポンスする
w.WriteHeader(http.StatusInternalServerError)
s := `{"status":"500 Internal Server Error"}`
if _, err := w.Write([]byte(s)); err != nil {
log.Println(err)
}
return
}
// レスポンスの書き込み
w.WriteHeader(code)
if _, err := w.Write(b); err != nil {
log.Println(err)
}
ヘルパー関数
JSONレスポンスの書き込みは各ハンドラで実行するため、ヘルパー関数を作成します。
func getTask(w http.ResponseWriter, r *http.Request) {
// データの取得処理は割愛
// データがなかった場合
if !found {
writeJSONError(w, http.StatusNotFound, "")
}
t := Task{
ID: "1",
Title: "shopping",
}
writeJSON(w, http.StatusOK, t)
}
func writeJSON(w http.ResponseWriter, code int, v interface{}) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
b, err := json.Marshal(v)
if err != nil {
log.Println(err)
w.WriteHeader(http.StatusInternalServerError)
s := `{"status":"500 Internal Server Error"}`
if _, err := w.Write([]byte(s)); err != nil {
log.Println(err)
}
return
}
w.WriteHeader(code)
if _, err := w.Write(b); err != nil {
log.Println(err)
}
}
func writeJSONError(w http.ResponseWriter, code int, message string) {
s := fmt.Sprintf("%d %s", code, http.StatusText(code))
m := map[string]string{"status": s}
if message != "" {
m["message"] = message
}
writeJSON(w, code, m)
}