Le guide ultime des redirections Django

Le guide ultime des redirections Django

Lorsque vous créez une application Web Python avec le Django framework, vous devrez à un moment donné rediriger l’utilisateur d’une URL vers une autre.

Dans ce guide, vous apprendrez tout ce que vous devez savoir sur les redirections HTTP et comment les gérer dans Django. À la fin de ce didacticiel, vous allez:

  • Pouvoir rediriger un utilisateur d’une URL vers une autre URL

  • Connaître la différence entre les redirections temporaires et permanentes

  • Évitez les pièges courants lorsque vous travaillez avec des redirections

Ce didacticiel suppose que vous êtes familiarisé avec les blocs de construction de base d’une application Django, comme views et https://docs.djangoproject .com/en/2.1/topics/http/urls/[Modèles d’URL].

Django réoriente: un exemple super simple

Dans Django, vous redirigez l’utilisateur vers une autre URL en renvoyant une instance de + HttpResponseRedirect + ou + HttpResponsePermanentRedirect + depuis votre vue. La façon la plus simple de le faire est d’utiliser la fonction https://docs.djangoproject.com/en/2.1/topics/http/shortcuts/#redirect [+ redirect () +] du module `+ django.shortcuts + ". Voici un exemple:

# views.py
from django.shortcuts import redirect

def redirect_view(request):
    response = redirect('/redirect-success/')
    return response

Appelez simplement + redirect () + avec une URL dans votre vue. Il renverra une classe + + HttpResponseRedirect +, que vous retournerez ensuite de votre vue.

Une vue renvoyant une redirection doit être ajoutée à votre + urls.py +, comme toute autre vue:

# urls.py
from django.urls import path

from .views import redirect_view

urlpatterns = [
    path('/redirect/', redirect_view)
    # ... more URL patterns here
]

En supposant qu’il s’agit du principal + urls.py + de votre projet Django, l’URL /redirect/ redirige maintenant vers /redirect-success/.

Pour éviter de coder en dur l’URL, vous pouvez appeler + redirect () + avec le nom d’une vue ou d’un modèle d’URL ou d’un modèle pour éviter de coder en dur l’URL de redirection. Vous pouvez également créer une redirection permanente en passant l’argument de mot clé + permanent = True +.

Cet article pourrait se terminer ici, mais il pourrait difficilement s’appeler «Le guide ultime des redirections Django». Nous examinerons de plus près la fonction + redirect () + dans une minute et entrerons également dans les moindres détails des codes d’état HTTP et des différentes classes + HttpRedirectResponse +, mais prenons un peu de recul et commençons par un question fondamentale.

Pourquoi rediriger

Vous vous demandez peut-être pourquoi vous souhaitez rediriger un utilisateur vers une URL différente en premier lieu. Pour avoir une idée de l’endroit où les redirections ont du sens, regardez comment Django lui-même incorpore les redirections dans les fonctionnalités que le framework fournit par défaut:

  • Lorsque vous n’êtes pas connecté et que vous demandez une URL qui nécessite une authentification, comme l’administrateur Django, Django vous redirige vers la page de connexion.

  • Lorsque vous vous connectez avec succès, Django vous redirige vers l’URL que vous avez demandée à l’origine.

  • Lorsque vous modifiez votre mot de passe à l’aide de l’administrateur Django, vous êtes redirigé vers une page qui indique que la modification a réussi.

  • Lorsque vous créez un objet dans l’administrateur Django, Django vous redirige vers la liste des objets.

À quoi ressemblerait une implémentation alternative sans redirection? Si un utilisateur doit se connecter pour afficher une page, vous pouvez simplement afficher une page qui dit quelque chose comme «Cliquez ici pour vous connecter». Cela fonctionnerait, mais ce serait gênant pour l’utilisateur.

Les raccourcisseurs d’URL comme http://bit.ly sont un autre exemple de cas où les redirections sont utiles: vous tapez une URL courte dans la barre d’adresse de votre navigateur, puis vous êtes redirigé vers une page avec une URL longue et peu maniable.

Dans d’autres cas, les redirections ne sont pas seulement une question de commodité. Les redirections sont un instrument essentiel pour guider l’utilisateur à travers une application Web. Après avoir effectué une sorte d’opération avec des effets secondaires, comme la création ou la suppression d’un objet, il est préférable de rediriger vers une autre URL pour éviter d’exécuter accidentellement l’opération deux fois.

Un exemple de cette utilisation des redirections est la gestion des formulaires, où un utilisateur est redirigé vers une autre URL après avoir soumis avec succès un formulaire. Voici un exemple de code qui illustre la manière dont vous gérez généralement un formulaire:

 1 from django import forms
 2 from django.http import HttpResponseRedirect
 3 from django.shortcuts import redirect, render
 4
 5 def send_message(name, message):
 6     # Code for actually sending the message goes here
 7
 8 class ContactForm(forms.Form):
 9     name = forms.CharField()
10     message = forms.CharField(widget=forms.Textarea)
11
12 def contact_view(request):
13     # The request method 'POST' indicates
14     # that the form was submitted
15     if request.method == 'POST':  # 1
16         # Create a form instance with the submitted data
17         form = ContactForm(request.POST)  # 2
18         # Validate the form
19         if form.is_valid():  # 3
20             # If the form is valid, perform some kind of
21             # operation, for example sending a message
22             send_message(
23                 form.cleaned_data['name'],
24                 form.cleaned_data['message']
25             )
26             # After the operation was successful,
27             # redirect to some other page
28             return redirect('/success/')  # 4
29     else:  # 5
30         # Create an empty form instance
31         form = ContactForm()
32
33     return render(request, 'contact_form.html', {'form': form})

Le but de cette vue est d’afficher et de gérer un formulaire de contact qui permet à l’utilisateur d’envoyer un message. Suivons-le pas à pas:

  1. Tout d’abord, la vue examine la méthode de demande. Lorsque l’utilisateur visite l’URL connectée à cette vue, le navigateur effectue une requête + GET +.

  2. Si la vue est appelée avec une requête + POST +, les données + POST + sont utilisées pour instancier un objet + ContactForm +.

  3. Si le formulaire est valide, les données du formulaire sont passées à + ​​send_message () +. Cette fonction n’est pas pertinente dans ce contexte et n’est donc pas présentée ici.

  4. Après l’envoi du message, la vue renvoie une redirection vers l’URL /success/. C’est l’étape qui nous intéresse. Par souci de simplicité, l’URL est codée en dur ici. Vous verrez plus tard comment éviter cela.

  5. Si la vue reçoit une requête + GET + (ou, pour être précis, tout type de requête qui n’est pas une requête + POST +), elle crée une instance de + ContactForm + et utilise + django.shortcuts.render () + `pour rendre le modèle + contact_form.html + `.

Si l’utilisateur frappe maintenant recharger, seule l’URL /success/ est rechargée. Sans la redirection, le rechargement de la page reviendrait à soumettre le formulaire et à envoyer un autre message.

Dans les coulisses: comment fonctionne une redirection HTTP

Vous savez maintenant pourquoi les redirections ont un sens, mais comment fonctionnent-elles? Récapitulons rapidement ce qui se passe lorsque vous saisissez une URL dans la barre d’adresse de votre navigateur Web.

Une introduction rapide sur HTTP

Supposons que vous ayez créé une application Django avec une vue «Hello World» qui gère le chemin «/hello/». Vous exécutez votre application avec le serveur de développement Django, donc l’URL complète est + http://127.0.0.1: 8000/hello/+.

Lorsque vous entrez cette URL dans votre navigateur, il se connecte au port + 8000 + sur le serveur avec l’adresse IP + 127.0.0.1 + et envoie une requête HTTP + GET + pour le chemin `/bonjour/ ". Le serveur répond avec une réponse HTTP.

HTTP est basé sur du texte, il est donc relativement facile de regarder en arrière entre le client et le serveur. Vous pouvez utiliser l’outil de ligne de commande https://curl.haxx.se/docs/manpage.html [+ curl +] avec l’option + - include + pour consulter la réponse HTTP complète, y compris les en-têtes, comme ça:

$ curl --include http://127.0.0.1:8000/hello/
HTTP/1.1 200 OK
Date: Sun, 01 Jul 2018 20:32:55 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 11

Hello World

Comme vous pouvez le voir, une réponse HTTP commence par une ligne d’état qui contient un code d’état et un message d’état. La ligne d’état est suivie d’un nombre arbitraire d’en-têtes HTTP. Une ligne vide indique la fin des en-têtes et le début du corps de réponse, qui contient les données réelles que le serveur souhaite envoyer.

HTTP redirige les codes d’état

À quoi ressemble une réponse de redirection? Supposons que le chemin /redirect/ est géré par + redirect_view () +, comme indiqué précédemment. Si vous accédez à + ​​http://127.0.0.1: 8000/redirect/+ avec + curl +, votre console ressemble à ceci:

$ curl --include http://127.0.0.1:8000/redirect/
HTTP/1.1 302 Found
Date: Sun, 01 Jul 2018 20:35:34 GMT
Server: WSGIServer/0.2 CPython/3.6.3
Content-Type: text/html; charset=utf-8
Location:/redirect-success/
X-Frame-Options: SAMEORIGIN
Content-Length: 0

Les deux réponses peuvent sembler similaires, mais il existe quelques différences clés. La redirection:

  • Renvoie un code d’état différent (+ 302 + contre + 200 +)

  • Contient un en-tête + Location + avec une URL relative

  • Se termine par une ligne vide car le corps de la réponse de redirection est vide

Le différenciateur principal est le code d’état. La spécification de la norme HTTP dit ce qui suit:

_ Le code d’état 302 (Trouvé) indique que la ressource cible réside temporairement sous un URI différent. Étant donné que la redirection peut être modifiée à l’occasion, le client doit continuer à utiliser l’URI de demande effective pour les demandes futures. Le serveur DEVRAIT générer un champ d’en-tête Location dans la réponse contenant une référence URI pour les différents URI. L’agent utilisateur PEUT utiliser la valeur du champ Emplacement pour la redirection automatique. (Source) _

En d’autres termes, chaque fois que le serveur envoie un code d’état de «+ 302 +», il dit au client: «Hé, pour le moment, la chose que vous recherchez se trouve à cet autre endroit.»

Une phrase clé de la spécification est «PEUT utiliser la valeur du champ Emplacement pour la redirection automatique». Cela signifie que vous ne pouvez pas forcer le client à charger une autre URL. Le client peut choisir d’attendre la confirmation de l’utilisateur ou décider de ne pas charger du tout l’URL.

Vous savez maintenant qu’une redirection n’est qu’une réponse HTTP avec un code d’état + 3xx + et un en-tête + Location +. La conclusion clé ici est qu’une redirection HTTP est comme n’importe quelle ancienne réponse HTTP, mais avec un corps vide, un code d’état 3xx et un en-tête + Location +.

C’est ça. Nous allons lier cela à Django momentanément, mais examinons d’abord deux types de redirections dans cette plage de codes d’état + 3xx + et voyons pourquoi elles sont importantes en matière de développement Web.

Temporaire vs Redirection permanente

La norme HTTP spécifie plusieurs codes d’état de redirection, tous dans la plage + 3xx +. Les deux codes d’état les plus courants sont «+301 Redirection permanente +» et «+302 Trouvé +».

Un code d’état +302 Found + indique une redirection temporaire. Une redirection temporaire indique: "Pour le moment, la chose que vous recherchez se trouve à cette autre adresse." Pensez-y comme une enseigne qui dit: «Notre magasin est actuellement fermé pour rénovation. Veuillez vous rendre dans notre autre magasin au coin de la rue. » Étant donné que cela n’est que temporaire, vous devriez vérifier l’adresse d’origine la prochaine fois que vous irez faire du shopping.

*Remarque:* Dans HTTP 1.0, le message pour le code d'état 302 était `+ Redirection temporaire +`. Le message a été changé en `+ Found +` dans HTTP 1.1.

Comme son nom l’indique, les redirections permanentes sont censées être permanentes. Une redirection permanente indique au navigateur: "La chose que vous recherchez n’est plus à cette adresse. Il est maintenant à cette nouvelle adresse, et il ne sera plus jamais à l’ancienne adresse. "

Une redirection permanente est comme une enseigne de magasin qui dit: «Nous avons déménagé. Notre nouveau magasin approche à grands pas. » Ce changement est permanent. La prochaine fois que vous voudrez vous rendre au magasin, vous irez directement à la nouvelle adresse.

*Remarque:* Les redirections permanentes peuvent avoir des conséquences inattendues. Terminez ce guide avant d'utiliser une redirection permanente ou passez directement à la section «Les redirections permanentes sont permanentes».

Les navigateurs se comportent de la même manière lors de la gestion des redirections: lorsqu’une URL renvoie une réponse de redirection permanente, cette réponse est mise en cache. La prochaine fois que le navigateur rencontre l’ancienne URL, il se souvient de la redirection et demande directement la nouvelle adresse.

La mise en cache d’une redirection enregistre une demande inutile et permet une expérience utilisateur meilleure et plus rapide.

En outre, la distinction entre les redirections temporaires et permanentes est pertinente pour l’optimisation des moteurs de recherche.

Redirige dans Django

Vous savez maintenant qu’une redirection n’est qu’une réponse HTTP avec un code d’état + 3xx + et un en-tête + Location +.

Vous pouvez créer une telle réponse vous-même à partir d’un objet régulier + HttpResponse +:

def hand_crafted_redirect_view(request):
  response = HttpResponse(status=302)
  response['Location'] = '/redirect/success/'
  return response

Cette solution est techniquement correcte, mais elle implique un peu de frappe.

La classe + HTTPResponseRedirect +

Vous pouvez vous épargner de la frappe avec la classe + HttpResponseRedirect +, une sous-classe de + HttpResponse +. Instanciez simplement la classe avec l’URL vers laquelle vous souhaitez rediriger comme premier argument, et la classe définira le bon état et l’en-tête Location:

def redirect_view(request):
  return HttpResponseRedirect('/redirect/success/')

Vous pouvez jouer avec la classe + HttpResponseRedirect + dans le shell Python pour voir ce que vous obtenez:

>>>

>>> from django.http import HttpResponseRedirect
>>> redirect = HttpResponseRedirect('/redirect/success/')
>>> redirect.status_code
302
>>> redirect['Location']
'/redirect/success/'

Il existe également une classe pour les redirections permanentes, qui porte bien son nom + HttpResponsePermanentRedirect +. Il fonctionne de la même manière que + HttpResponseRedirect +, la seule différence est qu’il a un code d’état de `+301 (déplacé en permanence) + '.

*Remarque:* Dans les exemples ci-dessus, les URL de redirection sont codées en dur. Le codage en dur des URL est une mauvaise pratique: si l'URL change, vous devez rechercher dans tout votre code et modifier toutes les occurrences. Corrigeons ça!

Vous pouvez utiliser https://docs.djangoproject.com/en/2.1/ref/urlresolvers/#reverse [+ django.urls.reverse () +] pour créer une URL, mais il existe un moyen plus pratique pour vous verra dans la section suivante.

La fonction + redirect () +

Pour vous faciliter la vie, Django propose la fonction de raccourci polyvalente que vous avez déjà vue dans l’introduction: https://docs.djangoproject.com/en/2.1/topics/http/shortcuts/#redirectirect`+django.shortcuts. rediriger () + `].

Vous pouvez appeler cette fonction avec:

Il prendra les mesures appropriées pour transformer les arguments en URL et retourner un + HTTPResponseRedirect +. Si vous passez + permanent = True +, il retournera une instance de + HttpResponsePermanentRedirect +, résultant en une redirection permanente.

Voici trois exemples pour illustrer les différents cas d’utilisation:

  1. Passer un modèle:

from django.shortcuts import redirect

def model_redirect_view(request):
    product = Product.objects.filter(featured=True).first()
    return redirect(product)

+ + redirect () + appellera + product.get_absolute_url () + et utilisera le résultat comme cible de redirection. Si la classe donnée, dans ce cas + Product +, n’a pas de méthode + get_absolute_url () +, cela échouera avec un + TypeError +. . Passer un nom d’URL et des arguments:

from django.shortcuts import redirect

def fixed_featured_product_view(request):
    ...
    product_id = settings.FEATURED_PRODUCT_ID
    return redirect('product_detail', product_id=product_id)

+ + redirect () + va essayer d’utiliser ses arguments donnés pour inverser une URL. Cet exemple suppose que vos modèles d’URL contiennent un modèle comme celui-ci:

path('/product/<product_id>/', 'product_detail_view', name='product_detail')
  1. Passer une URL:

from django.shortcuts import redirect

def featured_product_view(request):
    return redirect('/products/42/')

+ + redirect () + traitera toute chaîne contenant un / ou +. + comme une URL et l’utilisera comme cible de redirection.

La vue + RedirectView + basée sur la classe

Si vous avez une vue qui ne fait que renvoyer une redirection, vous pouvez utiliser la vue basée sur la classe `+ django.views.generic.base.RedirectView + `.

Vous pouvez adapter + RedirectView + à vos besoins grâce à divers attributs.

Si la classe a un attribut + .url +, il sera utilisé comme URL de redirection. Les espaces réservés de formatage de chaîne sont remplacés par des arguments nommés à partir de l’URL:

# urls.py
from django.urls import path
from .views import SearchRedirectView

urlpatterns = [
    path('/search/<term>/', SearchRedirectView.as_view())
]

# views.py
from django.views.generic.base import RedirectView

class SearchRedirectView(RedirectView):
  url = 'https://google.com/?q=%(term)s'

Le modèle d’URL définit un argument + term +, qui est utilisé dans + SearchRedirectView + pour construire l’URL de redirection. Le chemin /search/kittens/ dans votre application vous redirigera vers + https://google.com/? Q = kittens +.

Au lieu de sous-classer + RedirectView + pour remplacer l’attribut + url +, vous pouvez également passer l’argument de mot clé + url + à + ​​as_view () + dans votre + urlpatterns +:

#urls.py
from django.views.generic.base import RedirectView

urlpatterns = [
    path('/search/<term>/',
         RedirectView.as_view(url='https://google.com/?q=%(term)s')),
]

Vous pouvez également remplacer + get_redirect_url () + pour obtenir un comportement complètement personnalisé:

from random import choice
from django.views.generic.base import RedirectView

class RandomAnimalView(RedirectView):

     animal_urls = ['/dog/', '/cat/', '/parrot/']
     is_permanent = True

     def get_redirect_url(*args, **kwargs):
        return choice(self.animal_urls)

Cette vue basée sur une classe redirige vers une URL choisie au hasard dans + .animal_urls +.

+ django.views.generic.base.RedirectView + offre quelques crochets supplémentaires pour la personnalisation. Voici la liste complète:

  • + .url + + Si cet attribut est défini, il doit s’agir d’une chaîne avec une URL vers laquelle rediriger. S’il contient des espaces réservés de mise en forme de chaîne tels que +% (nom) s +, ils sont développés à l’aide des arguments de mot clé transmis à la vue.

  • + .pattern_name + + Si cet attribut est défini, il doit s’agir du nom d’un modèle d’URL vers lequel rediriger. Tous les arguments de position et de mot-clé transmis à la vue sont utilisés pour inverser le modèle d’URL.

  • + .permanent + ' + Si cet attribut est `+ True +, la vue renvoie une redirection permanente. Il vaut par défaut + False +.

  • + .query_string + + Si cet attribut est + True +, la vue ajoute toute chaîne de requête fournie à l’URL de redirection. S’il s’agit de "+ False +", qui est la valeur par défaut, la chaîne de requête est supprimée.

  • + get_redirect_url ( args, * kwargs) + + Cette méthode est responsable de la construction de l’URL de redirection. Si cette méthode renvoie + None +, la vue renvoie un état +410 Gone +. + L’implémentation par défaut vérifie d’abord + .url +. Il traite + .url + comme un "ancien style" format chaîne, en utilisant tous les paramètres d’URL nommés passés à la vue pour développer les spécificateurs de format nommés. + Si + .url + n’est pas défini, il vérifie si + .pattern_name + est défini. Si tel est le cas, il l’utilise pour inverser une URL avec tous les arguments de position et de mot-clé qu’il a reçus. + Vous pouvez modifier ce comportement comme vous le souhaitez en remplaçant cette méthode. Assurez-vous simplement qu’il renvoie une chaîne contenant une URL.

    *Remarque:* Les vues basées sur les classes sont un concept puissant mais peuvent être un peu difficiles à comprendre. Contrairement aux vues classiques basées sur les fonctions, où il est relativement simple de suivre le flux du code, les vues basées sur les classes sont constituées d'une hiérarchie complexe de mixins et de classes de base.

Un excellent outil pour donner un sens à une classe de vue basée sur une classe est le site Web Classy Class-Based Views.

Vous pouvez implémenter la fonctionnalité de + RandomAnimalView + de l’exemple ci-dessus avec cette simple vue basée sur les fonctions:

from random import choice
from django.shortcuts import redirect

def random_animal_view(request):
    animal_urls = ['/dog/', '/cat/', '/parrot/']
    return redirect(choice(animal_urls))

Comme vous pouvez le voir, l’approche basée sur les classes n’offre aucun avantage évident tout en ajoutant une certaine complexité cachée. Cela soulève la question: quand devriez-vous utiliser + RedirectView +?

Si vous souhaitez ajouter une redirection directement dans votre + urls.py +, utiliser + RedirectView + est logique. Mais si vous vous retrouvez en écrasant + get_redirect_url +, une vue basée sur les fonctions pourrait être plus facile à comprendre et plus flexible pour de futures améliorations.

Utilisation avancée

Une fois que vous savez que vous souhaitez probablement utiliser + django.shortcuts.redirect () +, la redirection vers une URL différente est assez simple. Mais il y a quelques cas d’utilisation avancés qui ne sont pas si évidents.

Passer des paramètres avec des redirections

Parfois, vous souhaitez transmettre certains paramètres à la vue vers laquelle vous redirigez. Votre meilleure option consiste à transmettre les données dans la chaîne de requête de votre URL de redirection, ce qui signifie rediriger vers une URL comme celle-ci:

http://example.com/redirect-path/?parameter=value

Supposons que vous souhaitiez rediriger de + some_view () + vers + product_view () +, mais passez un paramètre facultatif + category +:

from django.urls import reverse
from urllib.parse import urlencode

def some_view(request):
    ...
    base_url = reverse('product_view')  # 1/products/
    query_string =  urlencode({'category': category.id})  # 2 category=42
    url = '{}?{}'.format(base_url, query_string)  # 3/products/?category=42
    return redirect(url)  # 4

def product_view(request):
    category_id = request.GET.get('category')  # 5
    # Do something with category_id

Le code de cet exemple est assez dense, suivons-le pas à pas:

  1. Tout d’abord, vous utilisez + django.urls.reverse () + pour obtenir le mappage d’URL vers + product_view () +.

  2. Ensuite, vous devez créer la chaîne de requête. C’est la partie après le point d’interrogation. Il est conseillé d’utiliser + urllib.urlparse.urlencode () + pour cela, car il se chargera d’encoder correctement tous les caractères spéciaux.

  3. Vous devez maintenant joindre + base_url + et + query_string + avec un point d’interrogation. Une chaîne de format fonctionne bien pour cela.

  4. Enfin, vous passez + url + à + ​​django.shortcuts.redirect () + ou à une classe de réponse de redirection.

  5. Dans + product_view () +, votre cible de redirection, le paramètre sera disponible dans le dictionnaire + request.GET +. Le paramètre peut être manquant, vous devez donc utiliser + requests.GET.get ('category') + au lieu de + requests.GET ['category'] +. Le premier retourne + None + lorsque le paramètre n’existe pas, tandis que le second déclencherait une exception.

    *Remarque:* Assurez-vous de valider toutes les données que vous lisez à partir des chaînes de requête. Il peut sembler que ces données sont sous votre contrôle car vous avez créé l'URL de redirection.

En réalité, la redirection peut être manipulée par l’utilisateur et ne doit pas être approuvée, comme toute autre entrée utilisateur. Sans validation appropriée, un attaquant pourrait être en mesure d’obtenir un accès non autorisé.

Codes de redirection spéciaux

Django fournit des classes de réponse HTTP pour les codes d’état + 301 + et + 302 +. Ceux-ci devraient couvrir la plupart des cas d’utilisation, mais si jamais vous devez renvoyer les codes d’état + 303 +, + 307 + ou + 308 +, vous pouvez assez facilement créer votre propre classe de réponse. Il suffit de sous-classer + HttpResponseRedirectBase + et d’écraser l’attribut + status_code +:

class HttpResponseTemporaryRedirect(HttpResponseRedirectBase):
    status_code = 307

Vous pouvez également utiliser la méthode + django.shortcuts.redirect () + pour créer un objet de réponse et modifier la valeur de retour. Cette approche est logique lorsque vous avez le nom d’une vue ou d’une URL ou d’un modèle vers lequel vous souhaitez rediriger:

def temporary_redirect_view(request):
    response = redirect('success_view')
    response.status_code = 307
    return response
*Remarque:* Il existe en fait une troisième classe avec un code d'état dans la plage `+ 3xx +`: `+ HttpResponseNotModified +`, avec le code d'état `+ 304 +`. Il indique que l'URL du contenu n'a pas changé et que le client peut utiliser une version mise en cache.

On pourrait soutenir que la réponse «+304 non modifié +» redirige vers la version mise en cache d’une URL, mais c’est un peu exagéré. Par conséquent, il n’est plus répertorié dans la section -Redirection 3xx ” de la norme HTTP.

Les pièges

Des redirections qui ne redirigeront tout simplement pas

La simplicité de + django.shortcuts.redirect () + peut être trompeuse. La fonction elle-même n’effectue pas de redirection: elle renvoie simplement un objet de réponse de redirection. Vous devez renvoyer cet objet de réponse depuis votre vue (ou dans un middleware). Sinon, aucune redirection ne se produira.

Mais même si vous savez qu’il ne suffit pas d’appeler + redirect () +, il est facile d’introduire ce bogue dans une application qui fonctionne grâce à une simple refactorisation. Voici un exemple pour illustrer cela.

Supposons que vous construisez une boutique et que vous ayez une vue responsable de l’affichage d’un produit. Si le produit n’existe pas, vous redirigez vers la page d’accueil:

def product_view(request, product_id):
    try:
        product = Product.objects.get(pk=product_id)
    except Product.DoesNotExist:
        return redirect('/')
    return render(request, 'product_detail.html', {'product': product})

Vous voulez maintenant ajouter une deuxième vue pour afficher les avis clients d’un produit. Il doit également rediriger vers la page d’accueil pour les produits non existants, donc dans un premier temps, vous extrayez cette fonctionnalité de + product_view () + dans une fonction d’aide + get_product_or_redirect () +:

def get_product_or_redirect(product_id):
    try:
        return Product.objects.get(pk=product_id)
    except Product.DoesNotExist:
        return redirect('/')

def product_view(request, product_id):
    product = get_product_or_redirect(product_id)
    return render(request, 'product_detail.html', {'product': product})

Malheureusement, après le refactoring, la redirection ne fonctionne plus.

Redirige sans arrêt

Lorsque vous traitez des redirections, vous pouvez accidentellement créer une boucle de redirection, en demandant à l’URL A de renvoyer une redirection qui pointe vers l’URL B qui renvoie une redirection vers l’URL A, etc. La plupart des clients HTTP détectent ce type de boucle de redirection et affichent un message d’erreur après un certain nombre de demandes.

Malheureusement, ce type de bogue peut être difficile à repérer car tout semble correct côté serveur. À moins que vos utilisateurs ne se plaignent du problème, la seule indication que quelque chose ne va pas est que vous avez un certain nombre de demandes d’un client qui aboutissent toutes à une réponse de redirection en succession rapide, mais aucune réponse avec un +200 OK + statut.

Voici un exemple simple de boucle de redirection:

def a_view(request):
    return redirect('another_view')

def another_view(request):
    return redirect('a_view')

Cet exemple illustre le principe, mais il est trop simpliste. Les boucles de redirection que vous rencontrerez dans la vie réelle seront probablement plus difficiles à repérer. Regardons un exemple plus élaboré:

def featured_products_view(request):
    featured_products = Product.objects.filter(featured=True)
    if len(featured_products == 1):
        return redirect('product_view', kwargs={'product_id': featured_products[0].id})
    return render(request, 'featured_products.html', {'product': featured_products})

def product_view(request, product_id):
    try:
        product = Product.objects.get(pk=product_id, in_stock=True)
    except Product.DoesNotExist:
        return redirect('featured_products_view')
    return render(request, 'product_detail.html', {'product': product})

+ Featured_products_view () + récupère tous les produits présentés, en d’autres termes, les instances + Product + avec + .featured + réglé sur + True +. Si un seul produit en vedette existe, il redirige directement vers + product_view () +. Sinon, il rend un modèle avec le jeu de requêtes + Featured_products +.

Le + product_view + semble familier de la section précédente, mais il a deux différences mineures:

  • La vue essaie de récupérer un + produit + qui est en stock, indiqué en ayant + .in_stock + défini sur + True +.

  • La vue redirige vers + Featured_products_view () + si aucun produit n’est en stock.

Cette logique fonctionne bien jusqu’à ce que votre boutique soit victime de son propre succès et que le produit en vedette que vous avez actuellement soit en rupture de stock. Si vous définissez + .in_stock + sur + False + mais oubliez de définir également + .featured + sur + False +, alors tout visiteur de votre + feature_product_view () + sera désormais coincé dans une boucle de redirection .

Il n’existe aucun moyen pare-balles pour éviter ce type de bogue, mais un bon point de départ est de vérifier si la vue vers laquelle vous redirigez utilise elle-même des redirections.

Les redirections permanentes sont permanentes

Les redirections permanentes peuvent être comme de mauvais tatouages: elles peuvent sembler être une bonne idée à l’époque, mais une fois que vous vous rendez compte qu’elles étaient une erreur, il peut être assez difficile de s’en débarrasser.

Lorsqu’un navigateur reçoit une réponse de redirection permanente pour une URL, il met cette réponse en cache indéfiniment. Chaque fois que vous demanderez l’ancienne URL à l’avenir, le navigateur ne se souciera pas de la charger et chargera directement la nouvelle URL.

Il peut être assez difficile de convaincre un navigateur de charger une URL qui a une fois renvoyé une redirection permanente. Google Chrome est particulièrement agressif en ce qui concerne la mise en cache des redirections.

Pourquoi cela peut-il être un problème?

Imaginez que vous souhaitiez créer une application Web avec Django. Vous enregistrez votre domaine sur + myawesomedjangowebapp.com + '. Dans un premier temps, vous installez une application de blog sur `+ https://myawesomedjangowebapp.com/blog/+ pour créer une liste de diffusion de lancement.

La page d’accueil de votre site sur + https://myawesomedjangowebapp.com/+ 'est toujours en construction, vous redirigez donc vers + https://myawesomedjangowebapp.com/blog/+'. Vous décidez d’utiliser une redirection permanente parce que vous avez entendu dire que les redirections permanentes sont mises en cache et que la mise en cache accélère les choses, et plus vite c’est mieux car la vitesse est un facteur de classement dans les résultats de recherche Google.

Il se trouve que vous n’êtes pas seulement un grand développeur, mais aussi un écrivain talentueux. Votre blog devient populaire et votre liste de diffusion de lancement s’allonge. Après quelques mois, votre application est prête. Il a maintenant une page d’accueil brillante et vous supprimez enfin la redirection.

Vous envoyez un e-mail d’annonce avec un code de réduction spécial à votre liste de diffusion de lancement importante. Vous vous penchez en arrière et attendez que les notifications d’inscription arrivent.

À votre horreur, votre boîte aux lettres se remplit de messages de visiteurs confus qui souhaitent visiter votre application mais sont toujours redirigés vers votre blog.

Que s’est-il passé? Les lecteurs de votre blog avaient visité + https://myawesomedjangowebapp.com/+ lorsque la redirection vers + https://myawesomedjangowebapp.com/blog/+ était toujours active. Comme il s’agissait d’une redirection permanente, elle a été mise en cache dans leurs navigateurs.

Quand ils ont cliqué sur le lien dans votre e-mail d’annonce de lancement, leur navigateur n’a jamais pris la peine de vérifier votre nouvelle page d’accueil et est allé directement sur votre blog. Au lieu de célébrer votre lancement réussi, vous êtes en train d’enseigner à vos utilisateurs comment jouer avec + chrome://net-internals + pour réinitialiser le cache de leurs navigateurs.

La nature permanente des redirections permanentes peut également vous mordre lors du développement sur votre machine locale. Revenons au moment où vous avez implémenté cette redirection fatidique permanente pour myawesomedjangowebapp.com.

Vous démarrez le serveur de développement et ouvrez + http://127.0.0.1: 8000/+. Comme prévu, votre application redirige votre navigateur vers + http://127.0.0.1: 8000/blog/+. Satisfait de votre travail, vous arrêtez le serveur de développement et allez déjeuner.

Vous revenez avec le ventre plein, prêt à vous attaquer au travail du client. Le client souhaite apporter des modifications simples à sa page d’accueil. Vous devez donc charger le projet du client et démarrer le serveur de développement.

Mais attendez, que se passe-t-il ici? La page d’accueil est cassée, elle renvoie désormais un 404! En raison de l’effondrement de l’après-midi, il vous faut un certain temps pour remarquer que vous êtes redirigé vers + http://127.0.0.1: 8000/blog/+, qui n’existe pas dans le projet du client.

Pour le navigateur, peu importe que l’URL + http://127.0.0.1: 8000/+ serve désormais une application complètement différente. Tout ce qui compte pour le navigateur, c’est que cette URL a retourné une fois dans le passé une redirection permanente vers + http://127.0.0.1: 8000/blog/+.

La leçon à retenir de cette histoire est que vous ne devez utiliser des redirections permanentes que sur des URL que vous n’avez pas l’intention de réutiliser. Il y a une place pour les redirections permanentes, mais vous devez être conscient de leurs conséquences.

Même si vous êtes sûr d’avoir vraiment besoin d’une redirection permanente, c’est une bonne idée de mettre en œuvre une redirection temporaire en premier et de ne basculer vers son cousin permanent qu’une fois que vous êtes sûr à 100% que tout fonctionne comme prévu.

Les redirections non validées peuvent compromettre la sécurité

Du point de vue de la sécurité, les redirections sont une technique relativement sûre. Un attaquant ne peut pas pirater un site Web avec une redirection. Après tout, une redirection redirige simplement vers une URL qu’un attaquant pourrait simplement taper dans la barre d’adresse de son navigateur.

Cependant, si vous utilisez une sorte d’entrée utilisateur, comme un paramètre d’URL, sans validation appropriée en tant qu’URL de redirection, cela pourrait être utilisé abusivement par un attaquant pour une attaque de phishing. Ce type de redirection est appelé open ou redirection non validée.

Il existe des cas d’utilisation légitimes pour la redirection vers une URL qui est lue à partir de l’entrée utilisateur. Un exemple typique est la vue de connexion de Django. Il accepte un paramètre d’URL `+ suivant + 'qui contient l’URL de la page vers laquelle l’utilisateur est redirigé après la connexion. Pour rediriger l’utilisateur vers son profil après la connexion, l’URL peut ressembler à ceci:

https://myawesomedjangowebapp.com/login/?next=/profile/

Django valide le paramètre + next +, mais supposons une seconde que non.

Sans validation, un attaquant pourrait créer une URL qui redirige l’utilisateur vers un site Web sous son contrôle, par exemple:

https://myawesomedjangowebapp.com/login/?next=https://myawesomedjangowebapp.co/profile/

Le site Web `+ myawesomedjangowebapp.co + 'pourrait alors afficher un message d’erreur et inciter l’utilisateur à saisir à nouveau ses informations d’identification.

La meilleure façon d’éviter les redirections ouvertes est de ne pas utiliser d’entrée utilisateur lors de la création d’une URL de redirection.

Si vous ne pouvez pas être sûr qu’une URL est sûre pour la redirection, vous pouvez utiliser la fonction + django.utils.http.is_safe_url () + pour la valider. La docstring explique assez bien son utilisation:

_ _ + is_safe_url (url, host = None, allowed_hosts = None, require_https = False) +

Renvoie + True + si l’url est une redirection sûre (c.-à-d. il ne pointe pas vers un hôte différent et utilise un schéma sécurisé). Renvoyez toujours + False + sur une URL vide. Si «+ require_https » est « True », seul «https» sera considéré comme un schéma valide, par opposition à «http» et «https» avec la valeur par défaut, « False +». (Source) _ _

Voyons quelques exemples.

Une URL relative est considérée comme sûre:

>>>

>>> # Import the function first.
>>> from django.utils.http import is_safe_url
>>> is_safe_url('/profile/')
True

Une URL pointant vers un autre hôte n’est généralement pas considérée comme sûre:

>>>

>>> is_safe_url('https://myawesomedjangowebapp.com/profile/')
False

Une URL pointant vers un autre hôte est considérée comme sûre si son hôte est fourni dans + allowed_hosts +:

>>>

>>> is_safe_url('https://myawesomedjangowebapp.com/profile/',
...             allowed_hosts={'myawesomedjangowebapp.com'})
True

Si l’argument + require_https + est + True +, une URL utilisant le schéma + http + n’est pas considérée comme sûre:

>>>

>>> is_safe_url('http://myawesomedjangowebapp.com/profile/',
...             allowed_hosts={'myawesomedjangowebapp.com'},
...             require_https=True)
False

Sommaire

Ceci termine ce guide sur les redirections HTTP avec Django. Félicitations: vous avez maintenant abordé tous les aspects des redirections depuis les détails de bas niveau du protocole HTTP jusqu’à la manière de haut niveau de les gérer dans Django.

Vous avez appris à quoi ressemble une redirection HTTP sous le capot, quels sont les différents codes d’état et comment les redirections permanentes et temporaires diffèrent. Cette connaissance n’est pas spécifique à Django et est précieuse pour le développement Web dans n’importe quelle langue.

Vous pouvez maintenant effectuer une redirection avec Django, soit en utilisant les classes de réponse de redirection + HttpResponseRedirect + et + HttpResponsePermanentRedirect +, soit avec la fonction de commodité `+ django.shortcuts.redirect () + '. Vous avez vu des solutions pour quelques cas d’utilisation avancés et savez comment éviter les pièges courants.

Si vous avez d’autres questions sur les redirections HTTP, laissez un commentaire ci-dessous et en attendant, bonne redirection!