概要
Djangoで標準のViewやDjango REST frameworkなどで作成したエンドポイントに対して、AngularJSのHttpProvider
でpostすると、403エラーが返ってくる。
$http.post('/api/entries', {'title': '日記', 'body': '今日はいい天気です'})
Response
{detail: "CSRF Failed: CSRF token missing or incorrect."}
原因
これは、AngularJSを使っていることはあまり関係なく、DjangoのCSRF Protectionを上手く回避できていないのが原因。
CSRF Tokenを格納するRequest Headerの値が、AngularJSがデフォルトで使用する値と、Djangoが認識する値で違っているのに由来しているらしい。
以下のドキュメントを読むと、Djangoでは、Ajaxリクエストをするときに、csrftoken
の値をX-CSRFToken
というヘッダ名で送信しないといけない。
Cross Site Request Forgery protection | Django documentation | Django
解決策
AngularJSのHTTPProvider
には、CSRF Protectionの挙動を変更するオプションが用意されており、これを指定してあげると、CSRF Tokenを送る際のヘッダ名や、参照するcookieのキーを変更できる。
Django向けの設定を行うには以下のように設定してあげると良い。
var myApp = angular.module('myApp', []).config(function($httpProvider) {
$httpProvider.defaults.xsrfCookieName = 'csrftoken'
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken'
});
これで、特に何もせずとも、AngularJSが自動的にcsrftoken
を解釈して送信してくれる。めでたい。
余談
DjangoでAngularJSを使うにはもう一ヶ所ハマりどころがあって、AngularJSの変数展開がDjangoのテンプレートエンジンと競合しているため、デフォルトでは変数展開を行うことができない。
下記を参照にシンボルを変更すると幸せになれる。