【问题标题】:Marking Notifications as Read with JS/Ajax API使用 JS/Ajax API 将通知标记为已读
【发布时间】:2017-10-04 09:00:58
【问题描述】:

我在我的网站中实现了 django-notifications,它运行良好 (https://github.com/django-notifications/django-notifications)。 我遇到的唯一也是我希望的最后一个问题是通知从未标记为已读。有可能通过 API 调用来做到这一点,所以我用 JS 写了一些东西。我能够获取数据,但我无法更改/发布它们,因为我总是得到 403。我可以在管理页面中选中/取消选中布尔值,但我无法通过 API 调用来做到这一点。有人知道我能做什么吗? “文档”只是 Github 上的一个站点。制作者只做了一些简短的例子,没有太多的上下文。

我的代码:

$(".notiBla").click(function(){
$.ajax({
    url:("/inbox/notifications/api/unread_list/?max=3&mark_as_read=false/"),
    dataType:'json',
    type: 'POST',
    data: "unread_list",
    success:function(response){
      response.unread_list[0].unread=false
      console.log(response.unread_list[0].unread)
    }
});
});

这将返回 403,因为我不允许进行 POST。当我使用 Get 时,它会正确显示数据,但这没用,因为我需要更改数据...... 有人知道答案或其他方式来实现我的目标吗?

好的,我刚刚发现了一条错误消息。它说:

Forbidden (CSRF token missing or incorrect.): /inbox/notifications/api/unread_list/
[05/May/2017 19:38:28] "POST /inbox/notifications/api/unread_list/?max=3&mark_as_read=false/ HTTP/1.1" 403 2502

但我还是有点迷茫,因为我不知道 CSRF 丢失在哪里。

【问题讨论】:

    标签: javascript ajax django notifications csrf


    【解决方案1】:

    来自文档:

    AJAX¶

    虽然上述方法可以用于 AJAX POST 请求,但它有一些不便之处:您必须记住在每个 POST 请求中将 CSRF 令牌作为 POST 数据传递。为此,有一种替代方法:在每个 XMLHttpRequest 上,将自定义 X-CSRFToken 标头设置为 CSRF 令牌的值。这通常更容易,因为许多 JavaScript 框架提供了允许在每个请求上设置标头的钩子。

    首先,您必须获得 CSRF 令牌。如何做到这一点取决于是否启用了 CSRF_USE_SESSIONS 设置。

    如果 CSRF_USE_SESSIONS 为 False,则获取令牌¶

    令牌的推荐来源是 csrftoken cookie,如果您如上所述为您的视图启用 CSRF 保护,则会设置它。

    注意

    CSRF 令牌 cookie 默认命名为 csrftoken,但您可以通过 CSRF_COOKIE_NAME 设置控制 cookie 名称。

    默认情况下,CSRF 标头名称为 HTTP_X_CSRFTOKEN,但您可以使用 CSRF_HEADER_NAME 设置对其进行自定义。 获取令牌很简单:

    // using jQuery
    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;
    }
    
    var csrftoken = getCookie('csrftoken');
    

    上面的代码可以通过使用JavaScript Cookie库代替getCookie来简化:

    var csrftoken = Cookies.get('csrftoken');
    

    注意

    CSRF 令牌也存在于 DOM 中,但前提是在模板中使用 csrf_token 明确包含。 cookie 包含规范令牌; CsrfViewMiddleware 将更喜欢 cookie 而不是 DOM 中的令牌。无论如何,如果令牌存在于 DOM 中,则保证您拥有 cookie,因此您应该使用 cookie! 警告

    如果您的视图未呈现包含 csrf_token 模板标签的模板,则 Django 可能不会设置 CSRF 令牌 cookie。这在表单被动态添加到页面的情况下很常见。为了解决这种情况,Django 提供了一个视图装饰器来强制设置 cookie:ensure_csrf_cookie()。 如果 CSRF_USE_SESSIONS 为 True 则获取令牌¶

    如果激活 CSRF_USE_SESSIONS,则必须在 HTML 中包含 CSRF 令牌并使用 JavaScript 从 DOM 中读取令牌:

    {% csrf_token %}
    <script type="text/javascript">
    // using jQuery
    var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
    </script>
    

    在 AJAX 请求上设置令牌¶

    最后,您必须在 AJAX 请求中实际设置标头,同时在 jQuery 1.5.1 及更高版本中使用 settings.crossDomain 保护 CSRF 令牌不被发送到其他域:

    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);
        }
    }
    });
    

    简单来说,就是在一个js文件中提供getCookie函数并设置csrftoken变量。 然后添加 ajaxSetup 函数,这样就可以了。

    【讨论】:

    • 嘿,谢谢你的回答。答案有效。我没有得到 403,但数据没有改变。就像一切都保持不变。当我在成功后执行 console.log 时,它显示我 unread = false 但是当我重新加载页面时它又是 true...
    • 谢谢。答案在 Django 文档中是正确的。关于那个未读 = true.. 如果没有进一步的数据,我实际上无法说什么。答案仅涵盖 csrf 令牌身份验证部分。你可以向我展示这个 ajax 请求正在调用的视图。
    【解决方案2】:

    要将 Ajax POST 发送到 Django 视图,您需要传递一个 CSRF 令牌:将此密钥添加到您的 .ajax() 对象:

    csrfmiddlewaretoken: "{{ csrf_token }}",
    

    Django 的{{ csrf_token }} 模板变量将输出一个标记字符串,例如"mytoken123456789"。不要与 &lt;form&gt; 元素内部的模板标签 {% csrf_token %} 混淆,它输出一个 HTML 输入元素:

    <input type="hidden" name='csrfmiddlewaretoken' value='mytoken123456789' />
    

    【讨论】:

    • 我正在使用 django 表单,所以我不能输入类型、名称、值。无论如何,感谢您的回答。它与 Fazils 的答案一起使用...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-05
    • 2021-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-15
    • 2015-02-04
    相关资源
    最近更新 更多