with framework example csrf_exempt ajax django rest django-rest-framework

example - Django Rest Framework, ajax POST funciona pero PATCH arroja CSRF Failed: CSRF token missing or incorrect



django rest framework request (2)

Estoy trasladando mi proyecto a Django Rest Framework para hacer un APT REST adecuado para mi proyecto, creo que ayuda mucho a diseñar la API y hacerlo robusto, pero me encuentro con un problema:

Tengo un modelo de entrada y ListCreateAPIView asociadas de ListCreateAPIView y RetrieveUpdateDestroyAPIView . Puedo publicar con éxito una nueva instancia de entrada en la lista a través de una solicitud de ajax y proporcionar el csrfmiddlewaretoken como lo haría en la vista normal de Django.

POST entries/

Ahora estoy tratando de aplicar un parche a una instancia existente usando el mismo csrfmiddlewaretoken manera:

PATCH entries/3

El código de estado de la respuesta es entonces 403 FORBIDDEN con el error CSRF Failed: CSRF token missing or incorrect aunque compré en firebux que csrfmiddlewaretoken está en los datos de solicitud.

No hago lo que está mal y no puedo averiguar en qué parte del código se rechazó la solicitud.

Nota: Puedo parchar el objeto con la API que se puede explorar de Django Rest Framework .

Espero que alguien pueda ayudar. Gracias. Olivier

EDITAR

Estaba investigando el código para ver dónde ocurre el rechazo de la solicitud PATCH y encontré en django.middleware.csrt.py lo siguiente:

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'', '''')

La segunda prueba falla porque no es una solicitud POST pero la información requerida está en request.DATA. Entonces parece que django no está dispuesto a aceptar la solicitud PATCH. ¿Cuál crees que sería la mejor manera de evitar esto?

¿Recomendaría utilizar un sistema de autenticación diferente (hay algunos en la documentación de Django-rest-framework)?

EDIT2

Encontré una solución: observé que la API que se puede navegar está enviando una solicitud POST pero con un parámetro _method = "PATCH", así que hice lo mismo con mi solicitud de Ajax y funciona bien.

No sé si es la manera correcta de hacerlo, ¡cualquier comentario y opinión es bienvenido!

EDIT3

Entonces, después de leer más, descubrí (ya lo sabía) que como algunos navegadores no admiten solicitudes como PUT, PATCH, DELETE, el camino a seguir es enviar una solicitud posterior con X-HTTP-Method-Override en el encabezado

Entonces, creo que el buen camino es hacer lo siguiente:

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


Esta no es una solución directa a su problema, pero esto debería proporcionar algún contexto y proporcionar una posible solución.

Django no es compatible con el método HTTP PATCH y descarta todos los datos, incluido el token CSRF. Una solución posible es cambiar el método a POST, forzar a Django a volver a procesar la solicitud y cambiar el método nuevamente. Esto es un poco sucio pero funciona, el código de ejemplo utilizado por Django Piston se proporciona aquí:

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

Utilicé esta solución con éxito (con algunas modificaciones) y, aunque se siente un poco sucia, parece ser una solución muy útil.

Alternativamente, podría usar sobrecargar el método Do en los datos POST. Una vez más, no es la solución más bonita, pero muy viable.

Me encantaría que alguien proponga una mejor solución.


Finalmente agrego esto como una respuesta.

Entonces, después de leer más, descubrí (ya lo sabía) que como algunos navegadores no admiten solicitudes como PUT, PATCH, DELETE, el camino a seguir es enviar una solicitud posterior con X-HTTP-Method-Override en el encabezado

Entonces, creo que el buen camino es hacer lo siguiente:

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