47

Would appreciate someone showing me how to make a simple POST request using JSON with Django REST framework. I do not see any examples of this in the tutorial anywhere?

Here is my Role model object that I'd like to POST. This will be a brand new Role that I'd like to add to the database but I'm getting a 500 error.

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

Here is my curl request at a bash terminal prompt:

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


http://localhost:8000/lakesShoreProperties/role

The URL

http://localhost:8000/lakesShoreProperties/roles

DOES work with a GET request, and I can pull down all the roles in the database, but I can not seem to create any new Roles. I have no permissions set. I'm using a standard view in 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

And in my urls.py for this app, the relevant url - view mappings are correct:

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

Error message is:

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

What is going on here and what is the fix for this? Is localhost a cross site request? I have added @csrf_exempt to RoleDetail and RoleList but it doesn't seem to change anything. Can this decorator even be added to a class, or does it have to be added to a method? Adding the @csrf_exempt decorate, my error becomes:

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'

Then I disabled CSRF throughtout the entire app, and I now get this message:

{"non_field_errors": ["Invalid data"]} when my JSON object I know is valid json. It's a non-field error, but I'm stuck right here.

Well, it turns out that my json was not valid?

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

vs

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

Having the enclosing brackets [], causes the POST request to fail. But using the jsonlint.com validator, both of my json objects validate.

Update: The issue was with sending the POST with PostMan, not in the backend. See https://stackoverflow.com/a/17508420/203312

Community
  • 1
  • 1
user798719
  • 8,591
  • 21
  • 77
  • 114

8 Answers8

36

CSRF is exempted by default in Django REST Framework. Therefore, curl POST request works fine. POSTMAN request call returned CSRF incorrect because POSTMAN included csrf token if it is found in Cookies. You can solve this by cleaning up Cookies.

Terry Lam
  • 812
  • 11
  • 8
25

It's from your REST Framework settings. in your settings.py file, your REST_FRAMEWORK should have the following.

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

This will set your REST Framework to use token authentication instead of csrf authentication. And by setting the permission to AllowAny, you can authenticate only where you want to.

Idris
  • 973
  • 2
  • 9
  • 26
pharingee
  • 461
  • 4
  • 7
  • This solved my issue too. I used `rest_framework.permissions.IsAuthenticated` instead of `AllowAny` and it works just fine. – wcyn Feb 18 '16 at 00:06
10

You probably need to send along the CSRF token with your request. Check out https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#csrf-ajax

Update: Because you've already tried exempting CSRF, maybe this could help (depending on which version of Django you're using): https://stackoverflow.com/a/14379073/977931

Community
  • 1
  • 1
stellarchariot
  • 2,672
  • 17
  • 28
  • Thanks. It looks like CSRF is just a bag of hurt for my purposes right now. Disabling makes my POST request work fine now. I'd like to keep CSRF enabled, but it doesn't play nice with Django REST Framework. – user798719 Jul 06 '13 at 23:22
  • https://avilpage.com/2019/02/django-tips-csrf-token-postman-curl.html helped me with this! – Blairg23 Jun 10 '19 at 19:10
10

OK, well now of course I take back what I said. CSRF does work as intended.

I was making a POST request using a chrome plugin called POSTMAN. My POST request fails with CSRF enabled.

But a curl POST request using

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

works fine... I had to take off the braces, i.e., [], and make sure there is a slash after the 's' in roles, i.e., roles/, and csrf enabled did not throw any errors.

I'm not sure what the difference between calling using POSTMAN is vs using curl, but POSTMAN is run in the web browser which is the biggest difference. That said, I disabled csrf for the entire class RoleList but one identical request works with Curl, but fails with POSTMAN.

user798719
  • 8,591
  • 21
  • 77
  • 114
4

To give an update on current status, and sum up a few answers:

AJAX requests that are made within the same context as the API they are interacting with will typically use SessionAuthentication. This ensures that once a user has logged in, any AJAX requests made can be authenticated using the same session-based authentication that is used for the rest of the website.

AJAX requests that are made on a different site from the API they are communicating with will typically need to use a non-session-based authentication scheme, such as TokenAuthentication.

Therefore, answers recommending to replace SessionAuthentication with TokenAuthentication may solve the issue, but are not necessarily totally correct.

To guard against these type of attacks, you need to do two things:

  1. Ensure that the 'safe' HTTP operations, such as GET, HEAD and OPTIONS cannot be used to alter any server-side state.

  2. Ensure that any 'unsafe' HTTP operations, such as POST, PUT, PATCH and DELETE, always require a valid CSRF token. If you're using SessionAuthentication you'll need to include valid CSRF tokens for any POST, PUT, PATCH or DELETE operations.

In order to make AJAX requests, you need to include CSRF token in the HTTP header, as described in the Django documentation.

Therefore, it is important that csrf is included in header, as for instance this answer suggests.

Reference: Working with AJAX, CSRF & CORS, Django REST framework documentation.

Community
  • 1
  • 1
Wtower
  • 15,424
  • 11
  • 94
  • 69
3

As you said your URL was

http://localhost:8000/lakesShoreProperties/roles

Postman has some issues with localhost. Sending the POST to 127.0.0.1:8000/your-api/endpoint instead did the trick for me.

Thosse
  • 41
  • 3
1

the old Postman is having a problem with csrf tokens because it does not working with cookies.

I suggest for you to switch to the new version of postman, it works with cookies and you will not face this problem again.

0

if you have set AllowAny permission and you facing with csrf issue

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

then placing following in the settings.py will resolve the issue

REST_SESSION_LOGIN = False
navyad
  • 3,064
  • 5
  • 36
  • 75