自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

FormDataをformタグではなく、オブジェクトにキーと値をセットした上でAjax送信

thumbnail

タイトルの説明。

通常、Ajaxでサーバーにリクエストを送信する時、下記のようにFormDataクラスを使用して送信する。

let form_elem   = "#form_area";
let data        = new FormData( $(form_elem).get(0) );
let url         = $(form_elem).prop("action");
let method      = $(form_elem).prop("method");

$.ajax({
    url: url,
    type: method,
    data: data,
    processData: false,
    contentType: false,
    dataType: 'json'
}).done( function(data, status, xhr ) { 
    //Done
}).fail( function(xhr, status, error) {
    //Fail
}); 

しかし、送信したいデータがいつもこのようにformタグで囲まれているとも限らない。

そこで、FormDataクラスに引数を入れずにオブジェクトを作り、オブジェクトにキーと値をセットしてAjax送信する。

FormDataクラスに引数を入れず、オブジェクトにキーと値をセットする。

下記のようにすれば、引数なしのFormDataから作られたオブジェクトにキーと値をセットできる。

let data    = new FormData();
data.set("comment","これは.set()によって追加されました");

console.log(data);

なお、FormDataオブジェクトのメソッドには.set()の他に.append()がある。

.set()は同じキーの場合は上書きされるが、.append()の場合は追加になる。そのため同じキーが複数存在するというおかしな状況になってしまうので、.set()のほうが望ましいと思われる。

他にもキーを指定して削除する.delete()があるので、下記リンクを参考に。

https://developer.mozilla.org/ja/docs/Web/API/FormData

オブジェクト内のキーと値の確認

FormDataオブジェクトにセットされたキーと値はfor ofを使えば参照できる。

for (let v of data ){ console.log(v); }
for (let v of data.entries() ){ console.log(v); }

いずれも出力される内容は同じである。

403エラーが出る。CSRFトークンはどうする?

ここで上記コードに倣ってAjaxリクエストを実行してもエラーが起こる。CSRFトークンがリクエストボディの中に含まれていないから403エラーが出てしまうのだ。

そこで、上記と同様に適当な場所からCSRFトークンの値を抜き取り、キーと値をセットして送信すればよい。もしくはDjangoであれば、Ajax送信時にCookieからトークンをセットしてくれる、下記スクリプトを予め実行しておく。

//Ajax実行前にセッションIDを送信するスクリプト
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i].trim();
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');
function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

結論

これでDjangoで、DRFのシリアライザクラスを使用しなくてもフォームクラスだけでバリデーションを行うことができる。

DRFのシリアライザはモデルのsaveメソッドを継承していないので、早い段階でフォームクラスに統一したい。

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ