J'essaie d'intégrer jQuery dans une application Web que je réalise avec le framework Django. Cependant, j'ai de la difficulté à essayer de faire un simple appel ajax
au travail. Mon fichier de modèle contenant le formulaire html et javascript permettant de gérer l'appel ajax se présente comme suit:
<script type="text/javascript">
$(document).ready(function() {
$( "#target" ).submit(function() {
console.log('Form was submitted');
$.ajax({
type: "POST",
url: "/hello/", // or just url: "/my-url/path/"
data: {
query: $( "#query" ).val()
},
success: function(data) {
console.log(data);
}
});
return false;
});
})
</script>
<form id="target" action="." method="post">{% csrf_token %}
<input id= "query" type="text" value="Hello there">
<input type="submit" value="Search Recent Tweets">
</form>
Mon views.py
censé gérer l'appel ajax ressemble à ceci:
from Django.core.context_processors import csrf
from Django.shortcuts import render_to_response
from Django.template.loader import get_template
from Django.template import Context,RequestContext
from Django.views.decorators.csrf import ensure_csrf_cookie
from Django.http import HttpResponse
# access resource
def hello(request):
c = {}
c.update(csrf(request))
if request.is_ajax():
t = get_template('template.html')
#html = t.render(Context({'result': 'hello world'}))
con = RequestContext(request, {'result': 'hello world'})
return render_to_response('template.html', c, con)
else:
return HttpResponse('Not working!')
J'ai essayé de suivre la documentation officielle sur Protection contre la falsification des requêtes intersites et j'ai également examiné plusieurs questions de stackoverflow traitant d'un problème similaire. J'ai inclus le {% csrf_token %}
dans mon fichier de modèle html
mais il ne semble toujours pas fonctionner. Je reçois une erreur dans la console suggérant l'échec de l'appel ajax:
POST http://127.0.0.1:8000/hello/ 403 (FORBIDDEN)
Comment puis-je transmettre la variable result
avec ma réponse http et obtenir le bon fonctionnement de l'appel ajax? Toute aide est très appréciée.
Edit-1
Je n’étais pas censé passer le jeton csrf
avec ma demande de publication. SO conformément à la documentation, j'ai ajouté le code suivant à mon modèle javascript:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
console.log(csrftoken);
//Ajax call
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Lorsque j'actualise la page HTML du modèle dans le navigateur, je reçois null
dans la console, ce qui suggère que le cookie n'est pas défini ou défini. Qu'est-ce que je rate?
Parce que vous n'avez pas posté le csrfmiddlewaretoken, Django vous interdit donc… .. ce document peut vous aider.
Pour les gars paresseux:
Premier cookie téléchargé: http://plugins.jquery.com/cookie/
Ajoutez-le à votre html:
<script src="{% static 'designer/js/jquery.cookie.js' %}"></script>
Vous pouvez maintenant créer une requête POST opérationnelle:
var csrftoken = $.cookie('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$.ajax(save_url, {
type : 'POST',
contentType : 'application/json',
data : JSON.stringify(canvas),
success: function () {
alert("Saved!");
}
})
Je trouve toutes les réponses précédentes sur place, mais mettons les choses en contexte.
La réponse 403 interdite provient du middleware CSRF (voir Protection contre la falsification de requêtes sur site ):
Par défaut, une réponse «403 interdite» est envoyée à l’utilisateur si une requête entrante échoue aux vérifications effectuées par CsrfViewMiddleware.
De nombreuses options sont disponibles. Je recommanderais de suivre la réponse de @fivef afin que jQuery ajoute l'en-tête X-CSRFToken
avant chaque demande AJAX avec $.ajaxSetup
.
Cette réponse nécessite le cookie jQuery plugin. Si cela n'est pas souhaitable, une autre possibilité consiste à ajouter:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
var csrftoken = getCookie('csrftoken');
MAIS: si le paramètre CSRF_COOKIE_HTTPONLY
est défini sur True, ce qui se produit souvent lorsque le intergiciel de sécurité le recommande, le cookie n’est pas présent, même si @ensure_csrf_cookie()
est utilisé. Dans ce cas, {% csrf_token %}
doit être fourni dans chaque formulaire, ce qui génère une sortie telle que <input name="csrfmiddlewaretoken" value="cr6O9...FUXf6" type="hidden">
. Donc, la variable csrfToken
serait simplement obtenue avec:
var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();
Encore une fois, $.ajaxSetup
serait requis, bien sûr.
Les autres options disponibles mais non recommandées consistent à désactiver le middleware ou la protection csrf du formulaire spécifique avec @csrf_exempt()
.
Pour définir le cookie, utilisez le ensure_csrf_cookie
décorateur à votre vue:
from Django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def hello(request):
code_here()
Assurez-vous de ne pas mettre en cache la page/vue sur laquelle votre formulaire apparaît. Cela pourrait être la mise en cache de votre CSRF_TOKEN. Qui m'est arrivé!
La solution la plus rapide si vous n’intégrez pas js dans votre modèle est la suivante:
Placez <script type="text/javascript"> window.CSRF_TOKEN = "{{ csrf_token }}"; </script>
devant votre référence au fichier script.js dans votre modèle, puis ajoutez csrfmiddlewaretoken
dans votre dictionnaire data
:
$.ajax({
type: 'POST',
url: somepathname + "do_it/",
data: {csrfmiddlewaretoken: window.CSRF_TOKEN},
success: function() {
console.log("Success!");
}
})
Si vous intégrez votre js au modèle, c’est aussi simple que: data: {csrfmiddlewaretoken: '{{ csrf_token }}'}
Essayez d'inclure ce décorateur sur votre code d'expédition
from Django.utils.decorators import method_decorator
from Django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
def dispatch(self, request, *args, **kwargs):
return super(LessonUploadWorkView,self).dispatch(request,*args,**kwargs)
Avec SSL/https et avec CSRF_COOKIE_HTTPONLY = False, csrftoken ne figure toujours pas dans le cookie, ni avec la fonction getCookie (nom) proposée dans Django Doc ni avec le jquery. cookie.js proposé par fivef .
Wtower summary est parfait et je pensais que cela fonctionnerait après la suppression de CSRF_COOKIE_HTTPONLY de settings.py mais ce n’est pas dans https!
Pourquoi csrftoken n'est pas visible dans document.cookie ???
Au lieu d'obtenir
"Django_language = fr; csrftoken = rDrGI5cp98MnooPIsygWIF76vuYTkDIt"
Je reçois seulement
"Django_language = fr"
POURQUOI? Comme SSL/https supprime X-CSRFToken des en-têtes Je pensais que c'était dû aux paramètres d'en-tête de proxy de Nginx, mais apparemment pas ... Une idée?
Contrairement à Django doc Notes , il semble impossible de travailler avec csrf_token dans des cookies avec https. La seule façon de passer csrftoken est de passer par le DOM en utilisant {% csrf_token%} en HTML et de l'obtenir dans jQuery en utilisant
var csrftoken = $('input[name="csrfmiddlewaretoken"]').val();
Il est alors possible de le transmettre à ajax soit par entête ( xhr.setRequestHeader ), soit par params .