Django ajaxでpostする。csrf_tokenもつける。

Djangoで、formを使わないでpostする。(jQuery使用)

  • 2019/04/27: getCookieとcsrf_tokenの誤字を修正
  • 2020/03/15: $.ajaxパラメータの誤字を修正

Djangoでのpostの解説は、formを使ったものが多い。

<form method="post">
    {% csrf_token %}
    <input id="some_value" type="text" name="some_name">
    <input id="submit_id" type="submit">
</form>

このような方法ばかり。

buttonを押して、ダイアログで確認後にpostする。という流れを行いたいので、 この方法では実現できないと思う。jQueryでやると、

Forbidden (CSRF token missing or incorrect.): 

が表示され、viewまで行かない。 jQueryでのリクエスト時に、csrf_tokenを埋め込む必要がある。

クロスサイトリクエストフォージェリ (CSRF) 対策 | Django documentation | Django に対応が書かれていた。

htmlはこうする。(抜粋)

<!-- formがいらなくなる -->
<button id="button_id">Submit</button>

formはなく、buttonを直接配置する。 次に、Javascriptをこのようにする。(抜粋)

// csrf_tokenの取得に使う
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 = jQuery.trim(cookies[i]);
            // 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;
}

function csrfSafeMethod(method) {
    // these HTTP methods do not require CSRF protection
    return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$(document).on("click", "#button_id", function() {
    var button = $(this);
    var csrf_token = getCookie("csrftoken");
    var rslt = window.confirm("Do you really want to do?");
    if (rslt) {
        $.ajax({
           type: "POST",
           url: "SomeUrl",
           data: {
               "key1": "value1",
               "k2": "v2",
           },
           contentType: "application/json",
           // 送信前にヘッダにcsrf_tokenを付与。
           beforeSend: function(xhr, settings) {
                if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                    xhr.setRequestHeader("X-CSRFToken", csrf_token);
                }
            },
            success: function(data) {
                alert(data);
            },
            error: function(xhr, status, error) {
                alert(status + "\n" + 
                        "Status: " + xhr.status + "\n" + error);
            }
        });
    }
});

あとはpythonコードのviewPOSTを判別して行いたい処理をすればいい。

def some_view(request):
    if request.method == 'POST':
        from django.http import QueryDict

        # request.bodyに入っている。
        dic = QueryDict(request.body, encoding='utf-8')
        v1 = dic.get('key1')
        v2 = dic.get('k2')

        r1, r2 = do_something(v1, v2)

        from json import dumps
        ret = dumps({'k1': r1, 'k2': r2})
        return HttpResponse(ret, content_type='application/json')
    else:
        return do_something_else()

request.POST.get('key')はパラメータとしては渡されていないので、使えないのだと思う。 (request.POSTは空になる)。

一手間かかるが、QueryDictを自前で作ればほぼ同じようにできる。

微妙に公式docsは読みづらいのが悩み。下のような書籍の方がいいかも。情報は更新されないが……

コメント

タイトルとURLをコピーしました