如何使用Django REST框架进行POST简单的JSON? CSRF令牌丢失或不正确

希望有人向我演示如何使用JSON和Django REST框架进行一个简单的POST请求。 我在教程的任何地方都没有看到这个例子吗?

这是我想要发布的我的angular色模型对象。 这将是一个全新的angular色,我想添加到数据库,但我得到一个500错误。

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

这是我在一个bashterminal提示符下的curl请求:

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

url

 http://localhost:8000/lakesShoreProperties/roles 

是否可以使用GET请求,并且可以取消数据库中的所有angular色,但似乎无法创build任何新的angular色。 我没有权限设置。 我在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-view映射是正确的:

 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." } 

这里发生了什么事,这是什么修复? 本地主机是一个跨站点请求? 我已经将@csrf_exempt添加到RoleDetailRoleList但似乎并没有改变任何东西。 这个装饰器甚至可以添加到类中,还是必须添加到方法中? 添加@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”:[“无效数据”]}当我知道我的JSON对象是有效的JSON。 这是一个非现场的错误,但我坚持在这里。

那么,事实certificate,我的JSON是无效的?

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

VS

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

将括号括起来,会导致POST请求失败。 但使用jsonlint.comvalidation器,我的两个json对象validation。

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

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

更新:因为你已经试图免除CSRF,也许这可能会有所帮助(取决于你使用的是哪个版本的Django): https : //stackoverflow.com/a/14379073/977931

在Django REST框架中,默认情况下CSRF是免除的。 因此,curl POST请求正常工作。 POSTMAN请求调用返回的CSRF不正确,因为POSTMAN在Cookies中包含csrf标记。 你可以通过清理Cookies来解决这个问题。

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

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

这将设置您的REST框架使用令牌authentication,而不是csrfauthentication。 通过将权限设置为AllowAny ,您只能在您想要的地方进行身份validation。

好吧,现在我当然收回我说的话。 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/ 

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

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

给出最新状态的更新,并总结几个答案:

在与它们交互的API的相同上下文中创build的AJAX请求通常使用SessionAuthentication 。 这可以确保一旦用户login,任何AJAX请求都可以使用基于会话的身份validation来进行身份validation,该身份validation用于网站的其他部分。

通常需要使用基于非会话的身份validationscheme(如TokenAuthentication在与其通信的API不同的站点上进行的AJAX请求。

因此,build议用TokenAuthentication代替SessionAuthentication答案可能解决问题,但不一定完全正确。

为了防范这种types的攻击,你需要做两件事情:

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

  2. 确保任何“不安全的”HTTP操作(例如POSTPUTPATCHDELETE )始终需要一个有效的CSRF标记。 如果您使用SessionAuthentication ,则需要为任何POSTPUTPATCHDELETE操作包含有效的CSRF令牌。

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

因此,在头文件中包含csrf是非常重要的,例如这个答案所暗示的。

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

正如你所说的你的url是

http://localhost:8000/lakesShoreProperties/roles

邮差与本地主机有一些问题。 发送POST到127.0.0.1:8000/your-api/endpoint而不是我的伎俩。

旧的邮差在csrf标记上有问题,因为它不能与cookie一起工作。

我build议你切换到新版本的邮递员 ,它与cookies工作,你不会再面临这个问题。

您也可以禁用CSRF来制作自己的中间件:

 class DisableCSRF(object): def process_request(self, request): setattr(request, '_dont_enforce_csrf_checks', True) 

并将这个中间件包含在MIDDLEWARE_CLASSES的settings.py文件中。

如果你已经设置了AllowAny权限,并且面临着csrf问题

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

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

 REST_SESSION_LOGIN = False