Django Rest Framework,ajax POST 工作,但 PATCH 抛出 CSRF 失败:CSRF 令牌丢失或不正确

Posted

技术标签:

【中文标题】Django Rest Framework,ajax POST 工作,但 PATCH 抛出 CSRF 失败:CSRF 令牌丢失或不正确【英文标题】:Django Rest Framework, ajax POST works but PATCH throws CSRF Failed: CSRF token missing or incorrect 【发布时间】:2014-05-13 19:52:54 【问题描述】:

我正在将我的项目移植到 Django Rest Framework 以便为我的项目制作适当的 REST Api,我认为这有助于设计 API 并使其健壮,但我遇到了一个问题:

我有一个入门模型和关联的ListCreateAPIViewRetrieveUpdateDestroyAPIView 视图。 我可以通过 ajax 请求成功地在列表中发布一个新条目实例,并提供 csrfmiddlewaretoken,就像我在常规 Django 视图中所做的那样。

    POST entries/

现在我正在尝试使用相同的 csrfmiddlewaretoken 将补丁应用到现有实例,如下所示:

    PATCH entries/3

然后响应状态代码是 403 FORBIDDEN vith error CSRF Failed: CSRF token missing or incorrect 尽管我在 firebux 中检查了 csrfmiddlewaretoken 在请求数据中。

我不知道有什么问题,我无法找出代码中的哪个请求被拒绝。

注意:我可以使用 Django Rest Framework 可浏览 api 修补对象。

我希望有人可以提供帮助。 谢谢。 奥利维尔

编辑

我正在深入研究代码以查看 PATCH 请求被拒绝的位置,我在django.middleware.csrt.py 中发现了以下内容:

        if csrf_token is None: #<--- csrf_token is defined
            # No CSRF cookie. For POST requests, we insist on a CSRF cookie,
            # and in this way we can avoid all CSRF attacks, including login
            # CSRF.
            return self._reject(request, REASON_NO_CSRF_COOKIE)

        # Check non-cookie token for match.
        request_csrf_token = ""
        if request.method == "POST": #<--- This fails but request_csrf_token is in request.DATA
            request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')

        if request_csrf_token == "":
            # Fall back to X-CSRFToken, to make things easier for AJAX,
            # and possible for PUT/DELETE.
            request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '')

第二个测试失败,因为它不是 POST 请求,但所需的信息在 request.DATA 中。 所以看来 django 并不热衷于接受 PATCH 请求。 您认为解决此问题的最佳方法是什么?

您是否建议使用不同的身份验证系统(在 Django-rest-framework 文档中有一些)?

EDIT2

我找到了一个解决方案: 我观察到可浏览的 api 实际上是发送一个 POST 请求,但带有一个参数 _method="PATCH",所以我对我的 ajax 请求做了同样的事情,它工作正常。

我不知道这样做是否正确,欢迎任何反馈和意见!

EDIT3

所以,在阅读更多之后,我发现(我已经有点知道了..)因为一些浏览器不支持 PUT、PATCH、DELETE 等请求,所以要走的路是使用 X-HTTP- 发送一个 post 请求标题中的方法覆盖。

所以我认为最好的方法是执行以下操作:

$.ajax(
   headers: 
    'X-HTTP-Method-Override': 'PATCH'
   ,
   type : "POST",
   ...
);

【问题讨论】:

【参考方案1】:

我终于添加了这个作为答案。

所以,在阅读了更多之后,我发现(我已经有点知道了..)因为有些浏览器不支持 PUT、PATCH、DELETE 等请求,所以要走的路是使用 X-HTTP- 发送一个 post 请求标题中的方法覆盖。

所以我认为最好的方法是执行以下操作:

$.ajax(
    headers: 
        'X-HTTP-Method-Override': 'PATCH'
    ,
    type : "POST",
...
);

【讨论】:

我认为这不再起作用:“在版本 3.3.0 之前,支持半扩展标头 X-HTTP-Method-Override 来覆盖请求方法。这种行为不再是核心,但如果需要,可以使用中间件添加。” django-rest-framework.org/topics/browser-enhancements【参考方案2】:

我也遇到同样的问题,学习@overlii解题方法。我使用 django rest 框架 web 界面做put/patch,我发现HTTP Request Headers 信息如下: HTTP Request Headers

从这张图片中,我们可以找到X-CSRFTOKEN 标头,所以我将ajax 标头信息设置如下:

$.ajax(
        headers: 
            'X-CSRFTOKEN': ' csrf_token '
        ,
        type: "PATCH",
        dataType: "json",
        url: "/api/path/",
        data: "",
        success: function(data)
                
        
);

我用这种方式发送补丁请求,发现可以正常运行!

【讨论】:

非常感谢!它就像一个魅力!我一直在寻找解决方案,但在其他任何地方都找不到。使用 DELETE 动词时,我在数据字段中设置了 csrfmiddlewaretoken,但它仅适用于标题中的 X-CSRFTOKEN。 POST 动词通常只在表单数据中使用 de csrf 标记。这种行为有原因吗?【参考方案3】:

这不是您问题的直接解决方案,但应该提供一些上下文并提供可能的解决方案。

Django 不支持 HTTP PATCH 方法并丢弃包括 CSRF 令牌在内的所有数据。一种可能的解决方法是将方法更改为 POST,强制 Django 重新处理请求并再次更改方法。这有点脏但有效,此处提供了Django Piston 使用的示例代码:

def coerce_put_post(request):
"""
Django doesn't particularly understand REST.
In case we send data over PUT, Django won't
actually look at the data and load it. We need
to twist its arm here.

The try/except abominiation here is due to a bug
in mod_python. This should fix it.
"""
if request.method == "PUT":
    # Bug fix: if _load_post_and_files has already been called, for
    # example by middleware accessing request.POST, the below code to
    # pretend the request is a POST instead of a PUT will be too late
    # to make a difference. Also calling _load_post_and_files will result 
    # in the following exception:
    #   AttributeError: You cannot set the upload handlers after the upload has been processed.
    # The fix is to check for the presence of the _post field which is set 
    # the first time _load_post_and_files is called (both by wsgi.py and 
    # modpython.py). If it's set, the request has to be 'reset' to redo
    # the query value parsing in POST mode.
    if hasattr(request, '_post'):
        del request._post
        del request._files

    try:
        request.method = "POST"
        request._load_post_and_files()
        request.method = "PUT"
    except AttributeError:
        request.META['REQUEST_METHOD'] = 'POST'
        request._load_post_and_files()
        request.META['REQUEST_METHOD'] = 'PUT'

    request.PUT = request.POST

我已成功使用此修复程序(进行了一些修改),虽然感觉有点脏,但它似乎是一个非常有用的解决方案。

或者,您可以在 POST 数据中使用 do 方法重载。同样不是最漂亮的解决方案,但非常可行。

我希望有人提出更好的解决方案。

【讨论】:

以上是关于Django Rest Framework,ajax POST 工作,但 PATCH 抛出 CSRF 失败:CSRF 令牌丢失或不正确的主要内容,如果未能解决你的问题,请参考以下文章

怎么安装django rest framework

django rest framework中文介绍

17-Django-Django REST framework-REST framework及RESTful简介

为啥 django-rest-framework 不显示 OneToOneField 数据 - django

Django:rest framework之分页(Pagination)

django使用rest_framework