コントローラーにおけるJSONフォーマットの指定
JavaScriptのみでAjaxを書く際に、レスポンスデータをJSONで受け取ることに苦労したので、備忘として書いておきます。
使用しているフレームワークは、Ruby on Raisです。
具体的には、次のようなコントローラーのrespond_to
メソッドにおいて、format.json
を選択させることが目的になります。
def create
@message = Message.new(message_params)
if @message.save
respond_to do |format|
format.html { redirect_to root_path }
format.json { render json: { text: @message.text, id: @message.id } }
end
end
end
この記事の結論を先に言うと、Acceptリクエストヘッダ
でレスポンスのフォーマットを指定するということになります(後述)。
分かっている方には、当たり前の内容かもしれません。
<実行した環境>
Rails 5.2.4.1
Ruby 2.5.1
(turbolinksは無効にしています)
一般的な指定の方法
まずは、一般的な指定の方法です。普通はこれで解決すると思います。
jQueryの場合
jQueryでは、dataType: "json"
の1行を入れれば、JSONフォーマットを指定できます。
$.ajax({
url: "/messages",
type: "POST",
data: {
message: {text: "hoge"}
},
dataType: "json" // レスポンスフォーマットをjson形式と指定する
})
JavaScriptの例(.jsonを付加する場合)
JavaScriptでは、次のように、open()メソッドのURL指定(第2引数)のところで"/messages.json"
とします。
xmlHttpRequest.open("POST", "/messages.json"); // urlの末尾に.jsonを付加することでレスポンスフォーマットを指定
xmlHttpRequest.responseType = "json"; // レスポンスデータの形式をjsonと指定(これはフォーマットの指定ではない)
xmlHttpRequest.setRequestHeader("Content-Type", "application/json");
xmlHttpRequest.setRequestHeader("X-CSRF-Token", token);
xmlHttpRequest.send(data);
このように、リクエストURLの末尾に.json
を付加すれば、JSONのフォーマットでレスポンスを得ることができます。
なお、サンプルコードの全体像を確認されたい場合は、RailsにおけるAjaxの実装を参照してください。
<勘違いしていたところ>
2行目のresponseTypeメソッドで、xmlHttpRequest.responseType = "json"
というように指定していますが、最初は、これをレスポンスのフォーマットの指定と勘違いしていました。
確認してみると、"json"と指定した場合のレスポンスデータは、{text: "hoge", id: 3}
という形式で戻り、指定しなかった場合には、{"text":"hoge","id":3}
という形で戻ってくるだけです。
つまり、responseTypeメソッドはフォーマットを指定するものではないということです。
リクエストヘッダで指定する方法
最後に、リクエストURLに、.json
を付加しないでレスポンスのフォーマットを指定する方法です。
リクエストヘッダに次の1行を追加すれば、JSONのフォーマットでレスポンスデータが届きます。
Accept: application/json
このAcceptリクエストヘッダ
は、クライアントが理解できるコンテンツタイプ(MIMEタイプ)をサーバに伝えるものです(参照情報:MDN Web Docs / Accept)。
jQueryのリクエストヘッダに、上記の指定がされていたことから、JavaScriptでも同様の指定をすることでうまく動作しました。
このリクエストヘッダを追加するには、JavaScriptに次の1行を入れます。
xmlHttpRequest.setRequestHeader("Accept", "application/json");
念のため、コードの全体像を示せば次のとおりです。
リクエストURLへの.json
の付加は不要となります。
window.addEventListener("load", function() {
let token = document.getElementsByName("csrf-token")[0].content; //セキュリティトークンの取得
document.getElementById("nmessage_input").addEventListener("submit", function(e) {
e.preventDefault();
let hashData = { message: { text: "hoge" } };
let data = JSON.stringify(hashData);
let xmlHttpRequest = new XMLHttpRequest(); // XMLHttpRequestオブジェクトの作成
xmlHttpRequest.open("POST", "/messages"); // urlの末尾への.jsonの付加は不要
xmlHttpRequest.responseType = "json";
xmlHttpRequest.setRequestHeader("Accept", "application/json"); // リクエストヘッダーを追加(クライアントが理解できるコンテンツタイプをサーバに通知)
xmlHttpRequest.setRequestHeader("Content-Type", "application/json"); // リクエストヘッダーを追加(リクエストをJSONで送信する旨)
xmlHttpRequest.setRequestHeader("X-CSRF-Token", token); // リクエストヘッダーを追加(セキュリティトークンの追加)
xmlHttpRequest.send(data); // sendメソッドでサーバに送信
xmlHttpRequest.onreadystatechange = function() {
if (xmlHttpRequest.readyState === 4) {
if (xmlHttpRequest.status === 200) {
// リクエストが成功した場合の処理を記載
} else {
// リクエストが失敗した場合の処理を記載
}
}
};
});
});
以上です。
何らかのお役にたてば幸いです。