48

希望有人向我展示如何使用带有 Django REST 框架的 JSON 发出简单的 POST 请求。我在教程的任何地方都没有看到任何这样的例子?

这是我想发布的角色模型对象。这将是一个全新的角色,我想将它添加到数据库中,但出现 500 错误。

{
    "name": "Manager", 
    "description": "someone who manages"
}

这是我在 bash 终端提示符下的 curl 请求:

curl -X POST -H "Content-Type: application/json" -d '[
{
    "name": "Manager", 
    "description": "someone who manages"
}]'


http://localhost:8000/lakesShoreProperties/role

网址

http://localhost:8000/lakesShoreProperties/roles

可以使用 GET 请求,我可以下拉数据库中的所有角色,但我似乎无法创建任何新角色。我没有设置权限。我在views.py 中使用标准视图

class RoleDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Role.objects.all()
    serializer_class = RoleSerializer
    format = None

class RoleList(generics.ListCreateAPIView): 
        queryset = Role.objects.all()
        serializer_class = RoleSerializer
        format = None

在我urls.py的这个应用程序中,相关的 url - 视图映射是正确的:

url(r'^roles/$', views.RoleList.as_view()),
url(r'^role/(?P<pk>[0-9]+)/$', views.RoleDetail.as_view()),

错误信息是:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

这里发生了什么,解决方法是什么?localhost 是跨站点请求吗?我已经添加@csrf_exemptRoleDetailRoleList但它似乎并没有改变任何东西。这个装饰器甚至可以添加到类中,还是必须添加到方法中?添加@csrf_exempt装饰,我的错误变成:

Request Method: POST
Request URL:    http://127.0.0.1:8000/lakeshoreProperties/roles/
Django Version: 1.5.1
Exception Type: AttributeError
Exception Value:    
'function' object has no attribute 'as_view'

然后我在整个应用程序中禁用了 CSRF,现在我收到以下消息:

{"non_field_errors": ["Invalid data"]} 当我知道我的 JSON 对象是有效的 json 时。这是一个非字段错误,但我被困在这里。

好吧,事实证明我的 json 无效?

{
    "name": "admin", 
    "description": "someone who administrates"
}

对比

[
    {
        "name": "admin",
        "description": "someone who administrates"
    }
]

包含括号 [] 会导致 POST 请求失败。但是使用 jsonlint.com 验证器,我的两个 json 对象都会验证。

更新:问题在于使用 PostMan 发送 POST,而不是在后端。见https://stackoverflow.com/a/17508420/203312

4

8 回答 8

38

CSRF 在 Django REST Framework 中默认被豁免。因此, curl POST 请求可以正常工作。POSTMAN 请求调用返回的 CSRF 不正确,因为如果在 Cookie 中找到 POSTMAN 包含 csrf 令牌。您可以通过清理 Cookie 来解决此问题。

于 2013-10-13T15:52:26.453 回答
25

它来自您的 REST 框架设置。在您的settings.py文件中,您REST_FRAMEWORK应该具有以下内容。

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
   'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.AllowAny',
    ),
}

这会将您的 REST 框架设置为使用令牌身份验证而不是 csrf 身份验证。并且通过将权限设置为AllowAny,您可以仅在您想要的地方进行身份验证。

于 2014-09-03T18:13:25.117 回答
10

好的,现在我当然收回我说的话。CSRF 确实按预期工作。

我正在使用一个名为 POSTMAN 的 chrome 插件发出 POST 请求。我的 POST 请求在启用 CSRF 的情况下失败。

但是使用 curl POST 请求

curl -X POST -H "Content-Type: application/json" -d '
{
    "name": "Manager",
    "description": "someone who manages"
}' http://127.0.0.1:8000/lakeshoreProperties/roles/

工作正常...我不得不取下大括号,即[],并确保角色中的's'之后有一个斜线,即角色/,并且启用csrf没有抛出任何错误。

我不确定使用 POSTMAN 调用与使用 curl 之间的区别是什么,但 POSTMAN 在 Web 浏览器中运行,这是最大的区别。也就是说,我为整个类 RoleList 禁用了 csrf,但一个相同的请求适用于 Curl,但使用 POSTMAN 失败。

于 2013-07-07T01:06:22.560 回答
10

您可能需要随请求一起发送 CSRF 令牌。查看https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#csrf-ajax

更新:因为您已经尝试过豁免 CSRF,也许这会有所帮助(取决于您使用的 Django 版本):https ://stackoverflow.com/a/14379073/977931

于 2013-07-06T22:37:39.280 回答
4

提供有关当前状态的更新,并总结一些答案:

在与其交互的 API 相同的上下文中发出的 AJAX 请求通常使用SessionAuthentication. 这确保了一旦用户登录,任何 AJAX 请求都可以使用与网站其余部分相同的基于会话的身份验证进行身份验证。

在与其通信的 API 不同的站点上发出的 AJAX 请求通常需要使用非基于会话的身份验证方案,例如TokenAuthentication.

因此,建议替换为的答案SessionAuthentication可能TokenAuthentication会解决问题,但不一定完全正确。

要防范此类攻击,您需要做两件事:

  1. 确保“安全”HTTP 操作(例如和GET)不能用于更改任何服务器端状态。HEADOPTIONS

  2. 确保任何“不安全”的 HTTP 操作,例如POST、和PUT,始终需要有效的 CSRF 令牌。如果您正在使用,则需要为任何 、 或 操作包含有效的CSRF令牌。PATCHDELETESessionAuthenticationPOSTPUTPATCHDELETE

为了发出 AJAX 请求,您需要在 HTTP 标头中包含 CSRF 令牌,如 Django 文档中所述。

因此,重要的是 csrf 包含在标题中,例如这个答案所暗示的。

参考:使用 AJAX、CSRF 和 CORS、Django REST 框架文档

于 2015-12-22T12:06:51.477 回答
3

正如你所说,你的网址是

http://localhost:8000/lakesShoreProperties/roles

邮递员对 localhost 有一些问题。相反,发送 POST127.0.0.1:8000/your-api/endpoint对我有用。

于 2014-02-27T17:20:12.480 回答
1

邮递员在使用 csrf 令牌时遇到问题,因为它不能使用 cookie。

我建议您切换到新版本的postman,它可以与 cookie 一起使用,您将不会再遇到这个问题。

于 2014-12-04T19:07:16.043 回答
0

如果您已设置AllowAny权限并且面临 csrf 问题

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny'
    ]
}

然后将以下内容放入settings.py将解决问题

REST_SESSION_LOGIN = False
于 2015-01-10T10:34:57.070 回答