2013-07-06 5 views
42

Byłbym wdzięczny za pokazanie mi, jak wykonać proste żądanie POST za pomocą JSON z frameworkiem Django REST. Nie widzę żadnych przykładów tego w tutorialu w dowolnym miejscu?Jak zrobić prosty JSON POST za pomocą Django REST Framework? Token CSRF jest niepoprawny lub niepoprawny

Oto mój obiekt modelu roli, który chciałbym POST. To będzie zupełnie nowa funkcja, którą chciałbym dodać do bazy danych, ale otrzymuję błąd 500.

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

Oto moja prośba curl w wierszu terminalu bash:

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 

działa z żądania GET i mogę ciągnąć w dół wszystkie role w w bazie danych, ale nie mogę utworzyć żadnych nowych ról. Nie mam ustawionych uprawnień. Używam standardowego widoku w 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 

I w moich urls.py dla tej aplikacji, odpowiednie url - widok przekształceń są prawidłowe:

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

Komunikat o błędzie jest:

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

Co się tu dzieje i jaka jest tego przyczyna? Czy localhost jest żądaniem między witrynami? Dodałem @csrf_exempt do RoleDetail i RoleList, ale wydaje się, że nic nie zmienia. Czy ten dekorator można dodać do klasy, czy też trzeba go dodać do metody? Dodanie @csrf_exempt dekoracji, mój błąd staje:

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' 

Potem niepełnosprawnych CSRF throughtout cała aplikacja, a ja teraz dostać tę wiadomość:

{ "non_field_errors": [ "nieprawidłowe dane"]} kiedy mój obiekt JSON, który znam, jest prawidłowy json. To błąd inny niż terenowy, ale utknąłem tutaj.

Cóż, okazało się, że mój json był nieważny?

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

vs

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

uwzględniając wsporniki obejmujących, [], powoduje żądanie POST niepowodzeniem. Ale używając walidatora jsonlint.com, oba moje obiekty json sprawdzają poprawność.

Aktualizacja: Problem polegał na wysłaniu testu POST za pomocą narzędzia PostMan, a nie w zapleczu. Zobacz https://stackoverflow.com/a/17508420/203312

+0

Czy 'RoleSerializer' coś zdefiniowałeś? Być może warto się nim zajrzeć. – stellarchariot

Odpowiedz

9

Prawdopodobnie musisz wysłać wraz z żądaniem token CSRF. Sprawdź https://docs.djangoproject.com/en/1.7/ref/contrib/csrf/#csrf-ajax

Aktualizacja: Ponieważ już próbowałeś zwalniając CSRF, może mogłoby to pomóc (w zależności od wersji Django używasz): https://stackoverflow.com/a/14379073/977931

+0

Dzięki. Wygląda na to, że CSRF jest teraz tylko bagażem dla moich celów. Wyłączenie sprawia, że ​​moje żądanie POST działa teraz poprawnie. Chciałbym włączyć CSRF, ale nie działa to dobrze z Django REST Framework. – user798719

+0

Zaktualizowano link do dokumentacji Django. – Duncan

+0

Najnowszy link to https://docs.djangoproject.com/en/dev/ref/csrf/#ajax – Wtower

10

OK, dobrze teraz oczywiście biorę to co Powiedziałem. CSRF działa zgodnie z przeznaczeniem.

Zrobiłem żądanie POST przy użyciu wtyczki chrome o nazwie POSTMAN. Moje żądanie POST kończy się niepowodzeniem z włączoną obsługą CSRF.

Ale curl żądanie POST przy użyciu

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

działa dobrze ... musiałem zdjąć szelki, czyli [], a następnie upewnij się, że jest to slash po 's' w rolach , tzn. włączone role/i csrf nie spowodowały błędów.

Nie jestem pewien, jaka jest różnica między wywołaniem za pomocą POSTMAN a używaniem curl, ale POSTMAN jest uruchamiany w przeglądarce internetowej, co jest największą różnicą. Powiedziałem, że wyłączyłem csrf dla całej klasy RoleList, ale jedno identyczne żądanie działa z Curl, ale kończy się POSTMAN.

33

CSRF jest domyślnie wyłączony w Django REST Framework. Dlatego zwijane żądanie POST działa poprawnie. POSTMAN wywołanie żądania zwróciło błąd CSRF, ponieważ POSTMAN zawierał token csrf, jeśli został znaleziony w ciasteczkach. Możesz to rozwiązać, czyszcząc Cookies.

+1

Dzięki za ten listonosz! To były całkowicie ciasteczka powodujące to dla mnie :) – Gezim

3

Jak powiedział adres URL był

http://localhost:8000/lakesShoreProperties/roles

Listonosz ma pewne problemy z localhost. Wysłanie POST na numer 127.0.0.1:8000/your-api/endpoint zamiast tego dla mnie pomogło.

25

To z ustawień REST Framework. w swoim pliku settings.py, twoje REST_FRAMEWORK powinny mieć następujące.

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

Spowoduje to ustawienie struktury REST do używania uwierzytelniania tokena zamiast uwierzytelniania csrf. I ustawiając zezwolenie na AllowAny, możesz uwierzytelniać tylko tam, gdzie chcesz.

+0

To również rozwiązało mój problem. Użyłem 'rest_framework.permissions.IsAuthenticated' zamiast' AllowAny' i działa dobrze. – wcyn

1

stary Postman ma problem z tokenami csrf, ponieważ nie działa z plikami cookie.

Proponuję, abyś przełączył się na nową wersję postman, działa on z ciasteczkami i nie będziesz musiał stawić czoła temu problemowi.

0

Jeśli ustawiono AllowAny pozwolenie i stoi z emisji

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

następnie umieszczenie następujących w settings.py będzie rozwiązać problem

REST_SESSION_LOGIN = False 
4

Aby dać aktualizację stanu obecnego, a suma CSRF kilka odpowiedzi:

Żądania AJAX, składane w ciągu ame kontekst, ponieważ interfejs API, z którym się komunikują, zwykle będzie używał SessionAuthentication. Gwarantuje to, że po zalogowaniu się użytkownika wszystkie wykonane żądania AJAX mogą zostać uwierzytelnione przy użyciu tego samego uwierzytelniania opartego na sesji, które jest używane dla reszty witryny.

Żądania AJAX, które są wysyłane w innej witrynie niż interfejs API, z którym się komunikują, będą zwykle wymagać użycia schematu uwierzytelniania niezwiązanego z sesją, takiego jak TokenAuthentication.

Dlatego odpowiedzi zalecające zastąpić SessionAuthentication z TokenAuthentication może rozwiązać ten problem, ale niekoniecznie są całkowicie poprawne.

Aby ustrzec się przed tego typu atakami, trzeba zrobić dwie rzeczy:

  1. Upewnić się, że „bezpieczne” HTTP operacje, takie jak GET, HEAD i OPTIONS nie mogą być używane do zmiany któregokolwiek stan po stronie serwera.

  2. upewnić, czy wszystkie 'niebezpieczne' operacje, takie jak HTTP, PUTPOST, PATCH i DELETE, zawsze wymagają prawidłowego tokena CSRF. Jeśli używasz SessionAuthentication, musisz podać poprawne tokeny CSRF dla wszystkich operacji POST, PUT, PATCH lub DELETE.

W celu dokonania żądań AJAX, trzeba to CSRF token w nagłówku HTTP, jak opisano w dokumentacji Django.

Dlatego ważne jest, aby csrf był zawarty w nagłówku, jak sugeruje na przykład this answer.

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

3

Można również wyłączyć CSRF zrobić własny middleware:

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

i informuje o tym middleware w pliku settings.py w MIDDLEWARE_CLASSES.