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