気ままなタンス*プログラミングなどのノートブック

プログラミングやRPGツクール、DTM、VOCALOIDについてのんびり書きます。

【Django】AjaxとCSRF_TOKENについて(昨日の続き)

AjaxでPOSTリクエストを送信する方法がDjangoドキュメントに書いていた。

  • CSRF対策のため、CSRFトークンは必須であることを覚えておく
  • 通常のやり方だと、不便なので、ヘッダー[X-CSRFToken]をセットするためのフック機能を追加する

以下URLのAJAXの部分を引用

クロスサイトリクエストフォージェリ (CSRF) 対策 — Django 1.4 documentation

あなたは、 すべての POST リクエストについて、 CSRF トークンを POST するデータに含めることを 覚えておかなければなりません。なので、別の方法が用意されています。それは、各 XMLHttpRequest に対して、 X-CSRFToken という独自ヘッダーに CSRF トークンの 値を設定することです。多くの JavaScript のフレームワークはすべてのリクエストに ついて、ヘッダーを設定するようなフック機能を提供しているので、この操作は多くの 場合、簡単に行うことができます。 jQuery の場合、 ajaxSend イベントを以下の ように記述します
jQuery(document).ajaxSend(function(event, xhr, settings) {
    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 sameOrigin(url) {
        // url could be relative or scheme relative or absolute
        var host = document.location.host; // host + port
        var protocol = document.location.protocol;
        var sr_origin = '//' + host;
        var origin = protocol + sr_origin;
        // Allow absolute or scheme relative URLs to same origin
        return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
            (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
            // or any other URL that isn't scheme relative or absolute i.e relative.
            !(/^(\/\/|http:|https:).*/.test(url));
    }
    function safeMethod(method) {
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }

    if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
        xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
    }
});

FullCalendarを利用した例

<!-- Djangoテンプレートの構造は割愛 -->
<link href="{% static 'css/fullcalendar.min.css' %}" rel="stylesheet">
<link href="{% static 'css/fullcalendar.print.css' %}" rel="stylesheet" media="print">
<script src="{% static 'js/fullcalendar.min.js' %}"></script>

<script type="text/javascript">
// X-CSRFTokenにセットするフックコード(引用元のコード)を書いていることを前提
// なお先頭は$(document).ajaxSend(function(event, xhr, settings) {に変更した
$(document).ready(function() {
    $('#calendar').fullCalendar({
        header: {
            left: 'prev, next, today',
            center: 'title',
            right: 'month, basicWeek, basicDay'
        },
        defaultDate: '2015-03-18',
        editable: true,
        eventLimit: true,

        events: {

            url: '{% url 'api:any_view' %}',
            type: 'POST',
            error: function() {
                console.log("error");
            },
            success: function() {
                console.log("ok");
            }
        }
    });
});
</script>

<div id="calendar" class="fc fc-ltr fc-unthemed"></div>

views.py

# coding: utf-8
def any_view(request):
    user = request.user
    start = request.POST["start"] # FullCalendarのパラメータ
    end = request.POST["end"] # FullCalendarのパラメータ
    
    any_list = Any.objects.filter(user__username=user.username,
                                   any_ymd__range=(start, end) )

    #... jsonを返却 [{'title':'any_event', 'start': '2015-03-20'}...]

urls.py(project)

    # 追記
    url(r'^api/', include('api.urls', namespace='api')),

urls.py(app)

from django.conf.urls import patterns, url
from api import any
urlpatterns = patterns('',
    url('^any/$', views.any_view, name='any_view'),
)

結果のコード(jsonは割愛したため、上記コードをそのまま書いてもこれにはならないが・・・)

f:id:rinne_grid2_1:20150320071809p:plain