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",
...
});