使用fetch post请求进行适当的Django CSRF验证

23

我正在尝试使用JavaScript的fetch库向我的Django应用程序提交表单。然而,无论我做什么,它仍然抱怨CSRF验证。

Ajax文档中提到了指定一个头文件,我已经尝试过了。

我还尝试从模板标记中抓取令牌并将其添加到表单数据中。

两种方法都似乎不起作用。

以下是包含表单值和标题的基本代码:

let data = new FormData();
data.append('file', file);;
data.append('fileName', file.name);
// add form input from hidden input elsewhere on the page
data.append('csrfmiddlewaretoken', $('#csrf-helper input[name="csrfmiddlewaretoken"]').attr('value'));
let headers = new Headers();
// add header from cookie
const csrftoken = Cookies.get('csrftoken');
headers.append('X-CSRFToken', csrftoken);
fetch("/upload/", {
    method: 'POST',
    body: data,
    headers: headers,
})

我能够使用JQuery实现这个,但想尝试使用fetch

3个回答

28

已经弄清楚了。问题是fetch默认情况下不会包括cookie

简单的解决方法是在请求中添加credentials: "same-origin",这样就可以使用(表单字段但不带头信息)。以下是可行的代码:

let data = new FormData();
data.append('file', file);;
data.append('fileName', file.name);
// add form input from hidden input elsewhere on the page
data.append('csrfmiddlewaretoken', $('#csrf-helper input[name="csrfmiddlewaretoken"]').attr('value'));
fetch("/upload/", {
    method: 'POST',
    body: data,
    credentials: 'same-origin',
})

1
谢谢,这是我迄今为止见过的最简单的解决方案,而且它非常有效。 - Lewy Blue
@Dr.DOOM 我认为是这样的,你只需要使用 Flask 特定的方式来访问令牌并将其传递给视图即可。 - Cory

24

你的问题非常接近成功。如果你不想使用表单方法,这里有一种 json 方法。顺便说一下,@Cory的表单方法非常简洁。

  1. 使用第三方库的简洁方法
let data = {
    'file': file,
    'fileName': file.name,
};
// You have to download 3rd Cookies library
// https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
let csrftoken = Cookies.get('csrftoken');
let response = fetch("/upload/", {
    method: 'POST',
    body: JSON.stringify(data),
    headers: { "X-CSRFToken": csrftoken },
})

2. 另一种繁琐的方法,但不需要任何第三方库

let data = {
    'file': file,
    'fileName': file.name,
};
let csrftoken = getCookie('csrftoken');
let response = fetch("/upload/", {
    method: 'POST',
    body: JSON.stringify(data),
    headers: { "X-CSRFToken": csrftoken },
})

// The following function are copying from 
// https://docs.djangoproject.com/en/dev/ref/csrf/#ajax
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;
}

不知何故,Cory的接受答案对我无效。Abhinas提供的非常简单的方法——只需键入{{csrf_token}}也不起作用,但第二个方法却非常有效。我也不认为它特别麻烦,复制并粘贴一个JavaScript函数只是一点小代价,而且我绝对更喜欢这种方法而不是添加一个库。 - Werner Trelawney

5
这个问题有一个简单的解决方案,而不需要使用任何第三方库。在您的模板脚本中,这样做即可。
fetch("your_post_url",
        {
            method: "POST",
            body: JSON.stringify(data),
            headers: { "X-CSRFToken": '{{csrf_token}}' },
        }
    ).then(res => {
        //process your response
    }

我尝试了这个,但我仍然得到错误。在Python终端中的输出是Forbidden (CSRF token from the 'X-Csrftoken' HTTP header has incorrect length.): /tweet - waffledood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接