質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.01%

「Go言語」フォームから送信されたJSONをサーバー側で受け取れない..............

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 883

uk_63

score 23

はじめに

質問を読んでくださりありがとうございます。

環境
mac
go 1.11

Go言語の勉強のためにWebアプリケーションを作っています。
そこでブラウザのformからから送信されたデータをJSONに変換して、構造体にマッピングし、SQLに保存したいと考えています。しかし、ブラウザから送信されたデータをJSONに変換できず、詰まっています。

なので、まずはブラウザから送信されたデータをJSONに変換して構造体へマッピング、コンソール上に表示までができるようになりたいです。

試したこと

こちらの記事「jQueryでJSONをPOSTしてJSONのレスポンスを受け取る」を参考にしてJSONをフォームからの送信を試みましたが、失敗しました。

コード(追記 2/14)

クライアント側で、JSONデータをアラートで出すようにしたところ、クライアントから送信されたデータはJSONに変換されています。しかし、サーバー(GO)でそのJSONを受け取ってコンソールに出力しようとしたら、エラーが出ています。

レスポンスのボディを出力するようにfmt.Print(r.Body)とした結果が下記です。

&{0xc0000aa520 <nil> <nil> false true {0 0} false false false 0x129bc20}

またそのデータをデコードした結果、出力されたエラーが下記です。

invalid character 'i' in literal true (expecting 'r')

デコードしているGoのコード

func roomCreate(w http.ResponseWriter, r *http.Request) {
    var room model.Room
    if r.Body == nil {
        fmt.Printf("レスポンスボディは空です。\n")
    }
    err := json.NewDecoder(r.Body).Decode(&room)
    if err != nil {
        fmt.Printf("エラー内容\n「%v」\n", err)
    }
    fmt.Printf("データ\n%v\n", room)
    http.Redirect(w, r, "/room", 302)
}

下記が全体のコードになります。

    <form id="roomForm" method="POST" action="/room/new">
        <div class="form-group">
            <label>掲示板の名前</label>
            <input type="text" name="title" id="roomTitle" class="form-control">
        </div>
        <div class="form-group">
            <label>掲示板の説明</label>
            <textarea name="content" rows="3" id="roomContent" class="form-control"></textarea>
        </div>
        <button type="submit">作成する</button>
    </form>
$(function () {
    $("#roomForm").on('submit', function () {
        $("#roomForm").attr("disable", true);
        $.ajax({
            method: 'POST',
            url: '/routes_room.go',
            contentType: 'application/json',
            dataType: 'JSON',
            scriptCharset: 'utf-8',
            cache: 'false',
            timeout: 10000,
            data: {
                "title": $("#roomTitle").val(),
                "content": $("#roomContent").val()
            }
        })
        .done((data) => {
            alert(data)
        })
        .fail((data) => {
            alert(data)
        })
        .always(() => {
            $("#roomForm").attr("disable", false);
        });
    });
});
type Room struct {
    ID        int       `json:"id"`
    Title     string    `json:"subject"`
    Content   string    `json:"content"`
    CreatedAt time.Time `json:"created_at"`
}

func roomCreate(w http.ResponseWriter, r *http.Request) {
    var room model.Room
    if r.Body == nil {
        fmt.Printf("レスポンスボディは空です。\n")
    }
    err := json.NewDecoder(r.Body).Decode(&room)
    if err != nil {
        fmt.Printf("エラー内容\n「%v」\n", err)
    }
    fmt.Printf("データ\n%v\n", room)
    http.Redirect(w, r, "/room", 302)
}

func main() {
    mux := http.NewServeMux()

    files := http.FileServer(http.Dir("assets"))
    mux.Handle("/static/", http.StripPrefix("/static/", files))

    mux.HandleFunc("/", homeIndex)
    mux.HandleFunc("/room/", roomIndex)
    mux.HandleFunc("/room/new", roomCreate)

    server := http.Server{
        Addr:    "127.0.0.1:8080",
        Handler: mux,
    }
    log.Fatal(server.ListenAndServe())
}

コード(追記2)

フォームから送信すると、以前まで成功していたのが$.ajaxの結果がfailになっています。HTML, JSのコードがおかしくないかみていただけますか?

// actionの指定を消しました。 enctypeを追加しました。

<form id="roomForm" enctype="application/json" method="post">
        <div class="form-group">
            <label>掲示板の名前</label>
            <input type="text" name="title" value="Go言語コミュニティ" id="roomTitle" class="form-control">
        </div>
        <div class="form-group">
            <label>掲示板の説明</label>
            <textarea name="content" rows="3" id="roomContent" class="form-control"></textarea>
        </div>
        <button type="submit">作成する</button>
    </form>
// url の指定を変えました。
// JSON.stringify(data)を付け加えました。
// この処理の結果はなぜかfailになります。
$(function () {
    $("#roomForm").on('submit', function () {
        $("#roomForm").attr("disable", true);
        let data = {
            title: $("#roomTitle").val(),
            content: $("#roomContent").val()
        };
        $.ajax({
            method: 'post',
            url: '/room/new',
            data:JSON.stringify(data),
            dataType: 'json',
            contentType: "application/json",
            timeout: 10000,
        })
        .done((data) => {
            alert(data);
        })
        .fail((data) => {
            alert("ERROR");
            alert(data);
        })
        .always(() => {
            $("#roomForm").attr("disable", false);
        });
        return false
    });
});

すべてのバリデーションを通過し成功しました。

func roomCreate(w http.ResponseWriter, r *http.Request) {
    fmt.Print("roomCreate is starting!!\n")
    // validation
    if r.Method != "POST" {
        fmt.Printf("メソッド : \n%v\n", r.Method)
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    fmt.Print("バリデーション1 通過\n")

    if r.Header.Get("Content-Type") != "application/json" {
        fmt.Printf("レスポンスヘッダー : \n%v\n", r.Header.Get("Content-Type"))
        w.WriteHeader(http.StatusBadRequest)
        return
    }
    fmt.Print("バリデーション2 通過\n")

    //To allocate slice for request body
    length, err := strconv.Atoi(r.Header.Get("Content-Length"))
    if err != nil {
        fmt.Printf("コンテンツレングス : \n%v\n", r.Header.Get("Content-Length"))
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    fmt.Print("バリデーション3 通過\n")

    body := make([]byte, length)
    length, err = r.Body.Read(body)
    if err != nil && err != io.EOF {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    fmt.Print("バリデーション4 通過\n")

    var room model.Room
    err = json.Unmarshal(body[:length], &room)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        return
    }
    fmt.Print("バリデーション5 通過\n")
    fmt.Printf("%v\n", room)
    w.WriteHeader(http.StatusOK)
    http.Redirect(w, r, "/room", 302)
}

質問

なぜJSONをサーバー側で受け取れていないんでしょうか?

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+1

2/14追記を見ずに書いた回答

https://github.com/roman01la/JSONFormData こちらを利用するなどして
Javascript側でJSONにしてPOSTさせるのが一般的ですね。

(ちなみにformの属性enctype="application/json" というのはW3Cで検討中の仕様で
まだほとんどのブラウザは対応できていません。サポートされたらscript部分は不要になる予定。)

<form enctype="application/json" method="post">
  <input name="name" value="Bender" />
  <select name="hind">
    <option selected>Bitable</option>
    <option>Kickable</option>
  </select>
  <input type="checkbox" name="shiny" checked />
  <input type="submit" value="Test" />
</form>
<script src="./json-formdata.js"></script>
<script>
  Array.from(
    document.querySelectorAll("form[enctype='application/json']"),
    e => new JSONFormData(e, () => {})
  );
</script>

2/14追記への回答
いくつか指摘を

  • post先のurlがGoのソースファイルになっていますよ!(ハンドルされるURLでなければなりません)
  • r.Bodyがnilになることはないのでnilチェックは不要
  • fmt.Print(r.Body)ではポストされた内容は見れません。io.Copy(os.Stdout, r.Body)等を使いましょう。
  • 参考サイトの通りにdataを組めていません「JSON.stringify」が無いです。
  • また、submitイベントの伝搬を止めるためにsubmitコールバック記述の最後に「return false」が必要です。(これがない場合、ajaxによるポストの後に従来のformのsubmitが実行されます。)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/14 11:37

    JSのエラーが気になりますが、GOでJSONを受け取ることに成功しました。
    追記2にてコードを載せていますので、ご指摘いただけたら幸いです。

    キャンセル

  • 2019/02/14 12:42 編集

    1. ajaxのdataType指定はレスポンスのフォーマット指定です。
    2. ajaxリクエストの結果ステータスがリダイレクトの場合それも再リクエスト(通信を)を繰り返します。
    3. 最終的にajaxリクエストが受け取るレスポンスコンテンツは「/room」をGETした結果になる。
    4. 1.による指定でレスポンスコンテンツがJSONであることを期待しているのにHTMLが返されるのでJSONのデコードに失敗します。

    というように色んなことをいっぺんにやってくれるjQueryライブラリは便利ですがトラブルの原因をわかりにくくします。僕の示したサンプルではjQueryを使いません。jQueryを採用するのかしないのかどちらにするのかは決めてください。jQueryを使うのならjQueryのリファレンスをしっかり読みましょう。

    キャンセル

  • 2019/02/14 22:02

    丁寧にありがとう御座います。参考にさせていただきます。

    キャンセル

0

GO言語を使っている皆様はブラウザのフォームから送信されたデータをどのようにJSONへ変換されていますか?

サーバー(go)側でクライアントからPOSTされてきたフォームデータをJSONに変換すると言う事でしょうか?
質問の回答とは少々異なりますが、もしそうであればクライアントで側でJSONにしてからPOSTすると良いかと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/02/14 08:43

    回答ありがとうございます。
    クライアント側でJSONに変換する方法としては、jQuery でform の submit等にイベントを登録する。みたいな感じでしょうか?

    キャンセル

  • 2019/02/14 10:10

    クライアント側からJSONに変換することに成功しましたが、サーバー側(Go)で上手く受け取れていません。質問に追記しましたので、回答いただけませんか?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.01%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる