Der ultimative Leitfaden für Django-Weiterleitungen

Der ultimative Leitfaden für Django-Weiterleitungen

Wenn Sie eine Python-Webanwendung mitDjango frameworkerstellen, müssen Sie den Benutzer irgendwann von einer URL zu einer anderen umleiten.

In diesem Handbuch erfahren Sie alles, was Sie über HTTP-Weiterleitungen und den Umgang mit ihnen in Django wissen müssen. Am Ende dieses Tutorials werden Sie:

  • Sie können einen Benutzer von einer URL zu einer anderen URL umleiten

  • Kennen Sie den Unterschied zwischen temporären und permanenten Weiterleitungen

  • Vermeiden Sie häufige Fallstricke bei der Arbeit mit Weiterleitungen

In diesem Lernprogramm wird davon ausgegangen, dass Sie mit den Grundbausteinen einer Django-Anwendung wieviews undURL patterns vertraut sind.

Django Redirects: Ein super einfaches Beispiel

In Django leiten Sie den Benutzer zu einer anderen URL um, indem Sie eine Instanz vonHttpResponseRedirect oderHttpResponsePermanentRedirect aus Ihrer Ansicht zurückgeben. Der einfachste Weg, dies zu tun, besteht darin, die Funktionredirect() aus dem Moduldjango.shortcuts zu verwenden. Hier ist ein Beispiel:

# views.py
from django.shortcuts import redirect

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

Rufen Sie einfachredirect() mit einer URL in Ihrer Ansicht auf. Es wird eineHttpResponseRedirect-Klasse zurückgegeben, die Sie dann aus Ihrer Sicht zurückgeben.

Eine Ansicht, die eine Umleitung zurückgibt, muss wie jede andere Ansicht zu Ihrenurls.pyhinzugefügt werden:

# urls.py
from django.urls import path

from .views import redirect_view

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

Angenommen, dies ist das Haupturls.pyIhres Django-Projekts, leitet die URL/redirect/jetzt zu/redirect-success/ um.

Um eine harte Codierung der URL zu vermeiden, können Sieredirect() mit dem Namen einer Ansicht oder eines URL-Musters oder eines Modells aufrufen, um eine harte Codierung der Umleitungs-URL zu vermeiden. Sie können auch eine permanente Umleitung erstellen, indem Sie das Schlüsselwortargumentpermanent=True übergeben.

Dieser Artikel könnte hier enden, aber dann könnte er kaum als "Der ultimative Leitfaden für Django-Weiterleitungen" bezeichnet werden. Wir werden uns in einer Minute die Funktion vonredirect()genauer ansehen und uns auch mit den Details der HTTP-Statuscodes und der verschiedenen Klassen vonHttpRedirectResponsebefassen. Lassen Sie uns jedoch einen Schritt zurücktreten und mit einer Grundvoraussetzung beginnen Frage.

Warum umleiten?

Sie fragen sich vielleicht, warum Sie einen Benutzer überhaupt zu einer anderen URL umleiten möchten. Um eine Vorstellung davon zu bekommen, wo Weiterleitungen sinnvoll sind, schauen Sie sich an, wie Django selbst Weiterleitungen in Funktionen integriert, die das Framework standardmäßig bereitstellt:

  • Wenn Sie nicht angemeldet sind und eine URL anfordern, für die eine Authentifizierung erforderlich ist, wie z. B. der Django-Administrator, leitet Django Sie zur Anmeldeseite weiter.

  • Wenn Sie sich erfolgreich angemeldet haben, leitet Django Sie zu der ursprünglich angeforderten URL weiter.

  • Wenn Sie Ihr Passwort mit dem Django-Administrator ändern, werden Sie zu einer Seite weitergeleitet, die angibt, dass die Änderung erfolgreich war.

  • Wenn Sie ein Objekt im Django-Administrator erstellen, leitet Django Sie zur Objektliste weiter.

Wie würde eine alternative Implementierung ohne Weiterleitungen aussehen? Wenn sich ein Benutzer anmelden muss, um eine Seite anzuzeigen, können Sie einfach eine Seite mit der Aufschrift "Klicken Sie hier, um sich anzumelden" anzeigen. Dies würde funktionieren, wäre aber für den Benutzer unpraktisch.

URL-Shortener wiehttp://bit.ly sind ein weiteres Beispiel dafür, wo Weiterleitungen nützlich sind: Sie geben eine kurze URL in die Adressleiste Ihres Browsers ein und werden dann auf eine Seite mit einer langen, unhandlichen URL umgeleitet.

In anderen Fällen sind Weiterleitungen nicht nur eine Frage der Bequemlichkeit. Weiterleitungen sind ein wesentliches Instrument, um den Benutzer durch eine Webanwendung zu führen. Nach dem Ausführen einer Operation mit Nebenwirkungen wie dem Erstellen oder Löschen eines Objekts empfiehlt es sich, zu einer anderen URL umzuleiten, um zu verhindern, dass die Operation versehentlich zweimal ausgeführt wird.

Ein Beispiel für diese Verwendung von Weiterleitungen ist die Formularbehandlung, bei der ein Benutzer nach erfolgreichem Senden eines Formulars zu einer anderen URL umgeleitet wird. Hier ist ein Codebeispiel, das zeigt, wie Sie normalerweise mit einem Formular umgehen:

 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})

In dieser Ansicht wird ein Kontaktformular angezeigt und verarbeitet, mit dem der Benutzer eine Nachricht senden kann. Folgen wir ihm Schritt für Schritt:

  1. Zuerst betrachtet die Ansicht die Anforderungsmethode. Wenn der Benutzer die mit dieser Ansicht verbundene URL besucht, führt der Browser eineGET-Anforderung aus.

  2. Wenn die Ansicht mit einer Anforderung vonPOSTaufgerufen wird, werden die Daten vonPOSTverwendet, um ein Objekt vonContactFormzu instanziieren.

  3. Wenn das Formular gültig ist, werden die Formulardaten ansend_message() übergeben. Diese Funktion ist in diesem Zusammenhang nicht relevant und wird daher hier nicht gezeigt.

  4. Nach dem Senden der Nachricht gibt die Ansicht eine Umleitung zur URL/success/ zurück. Dies ist der Schritt, an dem wir interessiert sind. Der Einfachheit halber ist die URL hier fest codiert. Sie werden später sehen, wie Sie dies vermeiden können.

  5. Wenn die Ansicht eineGET-Anforderung empfängt (oder genauer gesagt jede Art von Anforderung, die keinePOST-Anforderung ist), erstellt sie eine Instanz vonContactForm und verwendetdjango.shortcuts.render(), um die Vorlagecontact_form.html zu rendern.

Wenn der Benutzer jetzt auf "Neu laden" klickt, wird nur die URL von/success/neu geladen. Ohne die Weiterleitung würde das erneute Laden der Seite das Formular erneut senden und eine weitere Nachricht senden.

Hinter den Kulissen: Wie eine HTTP-Umleitung funktioniert

Jetzt wissen Sie, warum Weiterleitungen sinnvoll sind, aber wie funktionieren sie? Lassen Sie uns kurz zusammenfassen, was passiert, wenn Sie eine URL in die Adressleiste Ihres Webbrowsers eingeben.

Eine kurze Einführung in HTTP

Nehmen wir an, Sie haben eine Django-Anwendung mit einer "Hallo Welt" -Ansicht erstellt, die den Pfad/hello/ verarbeitet. Sie führen Ihre Anwendung mit dem Django-Entwicklungsserver aus, sodass die vollständige URLhttp://127.0.0.1:8000/hello/ lautet.

Wenn Sie diese URL in Ihren Browser eingeben, wird eine Verbindung zu Port8000 auf dem Server mit der IP-Adresse127.0.0.1 hergestellt und eine HTTPGET-Anforderung für den Pfad/hello/ gesendet. Der Server antwortet mit einer HTTP-Antwort.

HTTP ist textbasiert, daher ist es relativ einfach, das Hin und Her zwischen Client und Server zu betrachten. Sie können das Befehlszeilentoolcurl mit der Option--include verwenden, um die vollständige HTTP-Antwort einschließlich der Header wie folgt anzuzeigen:

$ 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

Wie Sie sehen können, beginnt eine HTTP-Antwort mit einer Statuszeile, die einen Statuscode und eine Statusmeldung enthält. Auf die Statuszeile folgt eine beliebige Anzahl von HTTP-Headern. Eine leere Zeile zeigt das Ende der Header und den Beginn des Antwortkörpers an, der die tatsächlichen Daten enthält, die der Server senden möchte.

HTTP leitet Statuscodes um

Wie sieht eine Weiterleitungsantwort aus? Nehmen wir an, der Pfad/redirect/ wird vonredirect_view() behandelt (siehe oben). Wenn Sie mitcurl aufhttp://127.0.0.1:8000/redirect/ zugreifen, sieht Ihre Konsole folgendermaßen aus:

$ 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

Die beiden Antworten mögen ähnlich aussehen, es gibt jedoch einige wesentliche Unterschiede. Die Weiterleitung:

  • Gibt einen anderen Statuscode zurück (302 als200)

  • Enthält einenLocation-Header mit einer relativen URL

  • Endet mit einer leeren Zeile, da der Hauptteil der Umleitungsantwort leer ist

Das Hauptunterscheidungsmerkmal ist der Statuscode. Die Spezifikation des HTTP-Standards lautet wie folgt:

Der Statuscode 302 (Gefunden) gibt an, dass sich die Zielressource vorübergehend unter einem anderen URI befindet. Da die Umleitung gelegentlich geändert werden kann, sollte der Client den effektiven Anforderungs-URI weiterhin für zukünftige Anforderungen verwenden. Der Server sollte ein Standort-Header-Feld in der Antwort generieren, das eine URI-Referenz für die verschiedenen URI enthält. Der Benutzeragent KANN den Feldwert Standort für die automatische Umleitung verwenden. (Source)

Mit anderen Worten, wenn der Server einen Statuscode von302 sendet, sagt er dem Client: "Hey, im Moment befindet sich das gesuchte Objekt an diesem anderen Ort."

Ein Schlüsselbegriff in der Spezifikation lautet "KANN den Feldwert Standort für die automatische Umleitung verwenden." Dies bedeutet, dass Sie den Client nicht zwingen können, eine andere URL zu laden. Der Client kann wählen, ob er auf die Bestätigung des Benutzers warten oder die URL überhaupt nicht laden möchte.

Jetzt wissen Sie, dass eine Umleitung nur eine HTTP-Antwort mit einem Statuscode von3xxund einem Header vonLocationist. Der Schlüssel zum Erfolg ist, dass eine HTTP-Umleitung wie jede alte HTTP-Antwort ist, jedoch mit einem leeren Text, einem 3xx-Statuscode und einemLocation-Header.

Das ist es. Wir werden dies kurz auf Django zurückführen, aber zunächst werfen wir einen Blick auf zwei Arten von Weiterleitungen im Statuscodebereich von3xxund sehen, warum sie für die Webentwicklung von Bedeutung sind.

Temporär vs. Permanente Weiterleitungen

Der HTTP-Standard gibt mehrere Umleitungsstatuscodes an, die alle im Bereich von3xxliegen. Die beiden häufigsten Statuscodes sind301 Permanent Redirect und302 Found.

Ein Statuscode302 Found zeigt eine vorübergehende Umleitung an. In einer vorübergehenden Weiterleitung heißt es: "Im Moment befindet sich das gesuchte Objekt unter dieser anderen Adresse." Stellen Sie sich das wie ein Ladenschild vor, auf dem steht: „Unser Laden ist derzeit wegen Renovierungsarbeiten geschlossen. Bitte gehen Sie zu unserem anderen Geschäft um die Ecke. “ Da dies nur vorübergehend ist, überprüfen Sie beim nächsten Einkauf die ursprüngliche Adresse.

Note: In HTTP 1.0 war die Nachricht für den Statuscode 302Temporary Redirect. Die Nachricht wurde in HTTP 1.1 inFound geändert.

Wie der Name schon sagt, sollen permanente Weiterleitungen permanent sein. Eine permanente Weiterleitung teilt dem Browser mit: "Das gesuchte Objekt befindet sich nicht mehr an dieser Adresse. Es ist jetzt an dieser neuen Adresse und wird nie wieder an der alten Adresse sein. "

Eine permanente Weiterleitung ist wie ein Geschäftsschild mit der Aufschrift „Wir sind umgezogen. Unser neues Geschäft ist gleich um die Ecke. “ Diese Änderung ist dauerhaft. Wenn Sie das nächste Mal in den Laden gehen möchten, gehen Sie direkt zur neuen Adresse.

Note: Permanente Weiterleitungen können unbeabsichtigte Folgen haben. Beenden Sie diese Anleitung, bevor Sie eine permanente Weiterleitung verwenden, oder springen Sie direkt zum Abschnitt „Permanente Weiterleitungen sind permanent“.

Browser verhalten sich beim Umgang mit Weiterleitungen ähnlich: Wenn eine URL eine permanente Weiterleitungsantwort zurückgibt, wird diese Antwort zwischengespeichert. Wenn der Browser das nächste Mal auf die alte URL stößt, merkt er sich die Weiterleitung und fordert die neue Adresse direkt an.

Das Zwischenspeichern einer Weiterleitung erspart unnötige Anforderungen und sorgt für eine bessere und schnellere Benutzererfahrung.

Darüber hinaus ist die Unterscheidung zwischen temporären und permanenten Weiterleitungen für die Suchmaschinenoptimierung relevant.

Weiterleitungen in Django

Jetzt wissen Sie, dass eine Umleitung nur eine HTTP-Antwort mit einem Statuscode von3xxund einem Header vonLocationist.

Sie können eine solche Antwort selbst aus einem regulärenHttpResponse-Objekt erstellen:

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

Diese Lösung ist technisch korrekt, erfordert jedoch einiges an Eingabe.

DieHTTPResponseRedirect Klasse

Sie können sich das Schreiben mit der KlasseHttpResponseRedirect, einer Unterklasse vonHttpResponse, ersparen. Instanziieren Sie die Klasse einfach mit der URL, zu der Sie als erstes Argument umleiten möchten, und die Klasse legt den richtigen Status und den richtigen Speicherortkopf fest:

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

Sie können mit der KlasseHttpResponseRedirectin der Python-Shell spielen, um zu sehen, was Sie erhalten:

>>>

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

Es gibt auch eine Klasse für permanente Weiterleitungen, die treffendHttpResponsePermanentRedirect heißt. Es funktioniert genauso wieHttpResponseRedirect, der einzige Unterschied besteht darin, dass es einen Statuscode von301 (Moved Permanently) hat.

Note: In den obigen Beispielen sind die Umleitungs-URLs fest codiert. Das Hardcodieren von URLs ist eine schlechte Praxis: Wenn sich die URL jemals ändert, müssen Sie Ihren gesamten Code durchsuchen und alle Vorkommen ändern. Lass uns das beheben!

Sie könnendjango.urls.reverse() verwenden, um eine URL zu erstellen. Es gibt jedoch eine bequemere Möglichkeit, wie Sie im nächsten Abschnitt sehen werden.

Dieredirect() Funktion

Um Ihnen das Leben zu erleichtern, bietet Django die vielseitige Verknüpfungsfunktion, die Sie bereits in der Einführung gesehen haben:django.shortcuts.redirect().

Sie können diese Funktion aufrufen mit:

  • Eine Modellinstanz oder ein anderes Objekt mit einerget_absolute_url()-Methode

  • Ein URL- oder Ansichtsname sowie Positions- und / oder Schlüsselwortargumente

  • Eine URL

Es werden die entsprechenden Schritte ausgeführt, um die Argumente in eine URL umzuwandeln und einHTTPResponseRedirect zurückzugeben. Wenn Siepermanent=True übergeben, wird eine Instanz vonHttpResponsePermanentRedirect zurückgegeben, was zu einer permanenten Umleitung führt.

Hier sind drei Beispiele, um die verschiedenen Anwendungsfälle zu veranschaulichen:

  1. Ein Modell übergeben:

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

    redirect() ruftproduct.get_absolute_url() auf und verwendet das Ergebnis als Umleitungsziel. Wenn die angegebene Klasse, in diesem FallProduct, keineget_absolute_url()-Methode hat, schlägt dies mitTypeError fehl.

  2. Übergabe eines URL-Namens und von Argumenten:

    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() versucht, die angegebenen Argumente zu verwenden, um eine URL umzukehren. In diesem Beispiel wird davon ausgegangen, dass Ihre URL-Muster ein Muster wie das folgende enthalten:

    path('/product//', 'product_detail_view', name='product_detail')
  3. URL übergeben:

    from django.shortcuts import redirect
    
    def featured_product_view(request):
        return redirect('/products/42/')

    redirect() behandelt alle Zeichenfolgen, die/ oder. enthalten, als URL und verwendet sie als Umleitungsziel.

Die klassenbasierte Ansicht vonRedirectView

Wenn Sie eine Ansicht haben, die nichts anderes tut, als eine Umleitung zurückzugeben, können Sie die klassenbasierte Ansichtdjango.views.generic.base.RedirectView verwenden.

Sie könnenRedirectView über verschiedene Attribute an Ihre Bedürfnisse anpassen.

Wenn die Klasse ein.url-Attribut hat, wird es als Umleitungs-URL verwendet. Platzhalter für die Zeichenfolgenformatierung werden durch benannte Argumente aus der URL ersetzt:

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

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

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

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

Das URL-Muster definiert ein Argumentterm, das inSearchRedirectView zum Erstellen der Umleitungs-URL verwendet wird. Der Pfad/search/kittens/ in Ihrer Anwendung leitet Sie zuhttps://google.com/?q=kittens weiter.

AnstattRedirectView in eine Unterklasse umzuwandeln, um das Attributurl zu überschreiben, können Sie das Schlüsselwortargumenturl auch anas_view() inurlpatterns übergeben:

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

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

Sie können auchget_redirect_url() überschreiben, um ein vollständig benutzerdefiniertes Verhalten zu erhalten:

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)

Diese klassenbasierte Ansicht leitet zu einer URL weiter, die zufällig aus.animal_urls ausgewählt wurde.

django.views.generic.base.RedirectView bietet einige weitere Hooks zur Anpassung. Hier ist die vollständige Liste:

  • .url

    Wenn dieses Attribut festgelegt ist, sollte es sich um eine Zeichenfolge mit einer URL handeln, zu der umgeleitet werden soll. Wenn es Platzhalter für die Zeichenfolgenformatierung wie%(name)s enthält, werden sie mithilfe der an die Ansicht übergebenen Schlüsselwortargumente erweitert.

  • .pattern_name

    Wenn dieses Attribut festgelegt ist, sollte es der Name eines URL-Musters sein, zu dem umgeleitet werden soll. Alle an die Ansicht übergebenen Positions- und Schlüsselwortargumente werden verwendet, um das URL-Muster umzukehren.

  • .permanent

    Wenn dieses AttributTrue ist, gibt die Ansicht eine permanente Umleitung zurück. Der Standardwert istFalse.

  • .query_string

    Wenn dieses AttributTrue ist, hängt die Ansicht eine bereitgestellte Abfragezeichenfolge an die Umleitungs-URL an. Wenn diesFalse ist, was die Standardeinstellung ist, wird die Abfragezeichenfolge verworfen.

  • get_redirect_url(*args, **kwargs)

    Diese Methode ist für die Erstellung der Umleitungs-URL verantwortlich. Wenn diese MethodeNone zurückgibt, gibt die Ansicht den Status410 Gonezurück.

    Die Standardimplementierung überprüft zuerst.url. .url wird alsformat string im "alten Stil" behandelt, wobei alle benannten URL-Parameter verwendet werden, die an die Ansicht übergeben werden, um alle benannten Formatspezifizierer zu erweitern.

    Wenn.url nicht gesetzt ist, wird geprüft, ob.pattern_name gesetzt ist. Wenn dies der Fall ist, wird eine URL mit allen empfangenen Positions- und Schlüsselwortargumenten umgekehrt.

    Sie können dieses Verhalten beliebig ändern, indem Sie diese Methode überschreiben. Stellen Sie einfach sicher, dass eine Zeichenfolge mit einer URL zurückgegeben wird.

Note: Klassenbasierte Ansichten sind ein leistungsstarkes Konzept, es kann jedoch schwierig sein, den Kopf herumzureißen. Im Gegensatz zu regulären funktionsbasierten Ansichten, bei denen es relativ einfach ist, dem Code-Fluss zu folgen, bestehen klassenbasierte Ansichten aus einer komplexen Hierarchie von Mixins und Basisklassen.

Ein großartiges Werkzeug, um eine klassenbasierte Ansichtsklasse zu verstehen, ist die WebsiteClassy Class-Based Views.

Mit dieser einfachen funktionsbasierten Ansicht können Sie die Funktionalität vonRandomAnimalView aus dem obigen Beispiel implementieren:

from random import choice
from django.shortcuts import redirect

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

Wie Sie sehen können, bietet der klassenbasierte Ansatz keinen offensichtlichen Vorteil, während er eine versteckte Komplexität hinzufügt. Das wirft die Frage auf: Wann sollten SieRedirectView verwenden?

Wenn Sie eine Umleitung direkt in Ihreurls.py einfügen möchten, ist die Verwendung vonRedirectView sinnvoll. Wenn Sie jedochget_redirect_url überschreiben, ist eine funktionsbasierte Ansicht möglicherweise einfacher zu verstehen und für zukünftige Verbesserungen flexibler.

Fortgeschrittene Nutzung

Sobald Sie wissen, dass Sie wahrscheinlichdjango.shortcuts.redirect() verwenden möchten, ist die Umleitung zu einer anderen URL ganz einfach. Es gibt jedoch einige fortgeschrittene Anwendungsfälle, die nicht so offensichtlich sind.

Übergeben von Parametern mit Weiterleitungen

Manchmal möchten Sie einige Parameter an die Ansicht übergeben, zu der Sie umleiten. Am besten übergeben Sie die Daten in der Abfragezeichenfolge Ihrer Weiterleitungs-URL. Dies bedeutet, dass Sie zu einer URL wie der folgenden umleiten:

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

Angenommen, Sie möchten vonsome_view() nachproduct_view() umleiten, übergeben jedoch einen optionalen Parametercategory:

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

Der Code in diesem Beispiel ist ziemlich dicht. Folgen wir ihm also Schritt für Schritt:

  1. Zuerst verwenden Siedjango.urls.reverse(), um die URL-Zuordnung zuproduct_view() zu erhalten.

  2. Als nächstes müssen Sie die Abfragezeichenfolge erstellen. Das ist der Teil nach dem Fragezeichen. Es ist ratsam, dafürurllib.urlparse.urlencode() zu verwenden, da dafür gesorgt wird, dass Sonderzeichen ordnungsgemäß codiert werden.

  3. Jetzt müssen Siebase_url undquery_string mit einem Fragezeichen verbinden. Eine Formatzeichenfolge funktioniert dafür einwandfrei.

  4. Schließlich übergeben Sieurl andjango.shortcuts.redirect() oder an eine Umleitungsantwortklasse.

  5. Inproduct_view(), Ihrem Umleitungsziel, ist der Parameter im Wörterbuch vonrequest.GETverfügbar. Der Parameter fehlt möglicherweise, daher sollten Sierequests.GET.get('category') anstelle vonrequests.GET['category'] verwenden. Ersteres gibtNone zurück, wenn der Parameter nicht vorhanden ist, während letzteres eine Ausnahme auslösen würde.

Note: Stellen Sie sicher, dass Sie alle Daten überprüfen, die Sie aus Abfragezeichenfolgen gelesen haben. Es scheint, als ob diese Daten unter Ihrer Kontrolle stehen, da Sie die Weiterleitungs-URL erstellt haben.

In der Realität kann die Umleitung vom Benutzer manipuliert werden und darf nicht wie jede andere Benutzereingabe als vertrauenswürdig eingestuft werden. Ohne ordnungsgemäße Validierungan attacker might be able gain unauthorized access.

Spezielle Weiterleitungscodes

Django bietet HTTP-Antwortklassen für die Statuscodes301 und302. Diese sollten die meisten Anwendungsfälle abdecken. Wenn Sie jedoch jemals die Statuscodes303,307 oder308 zurückgeben müssen, können Sie ganz einfach Ihre eigene Antwortklasse erstellen. Unterklassen Sie einfachHttpResponseRedirectBase und überschreiben Sie das Attributstatus_code:

class HttpResponseTemporaryRedirect(HttpResponseRedirectBase):
    status_code = 307

Alternativ können Sie die Methodedjango.shortcuts.redirect()verwenden, um ein Antwortobjekt zu erstellen und den Rückgabewert zu ändern. Dieser Ansatz ist sinnvoll, wenn Sie den Namen einer Ansicht oder URL oder eines Modells haben, zu dem Sie umleiten möchten:

def temporary_redirect_view(request):
    response = redirect('success_view')
    response.status_code = 307
    return response

Note: Es gibt tatsächlich eine dritte Klasse mit einem Statuscode im Bereich von3xx:HttpResponseNotModified mit dem Statuscode304. Es zeigt an, dass sich die Inhalts-URL nicht geändert hat und dass der Client eine zwischengespeicherte Version verwenden kann.

Man könnte argumentieren, dass die Antwort von304 Not Modifiedauf die zwischengespeicherte Version einer URL umleitet, aber das ist ein bisschen langwierig. Folglich ist es nicht mehr in“Redirection 3xx” section des HTTP-Standards aufgeführt.

Fallstricke

Weiterleitungen, die nicht umgeleitet werden

Die Einfachheit vondjango.shortcuts.redirect() kann täuschen. Die Funktion selbst führt keine Umleitung durch, sondern gibt nur ein Umleitungsantwortobjekt zurück. Sie müssen dieses Antwortobjekt aus Ihrer Sicht (oder in einer Middleware) zurückgeben. Andernfalls erfolgt keine Umleitung.

Aber selbst wenn Sie wissen, dass es nicht ausreicht, nurredirect() aufzurufen, ist es einfach, diesen Fehler durch einfaches Refactoring in eine funktionierende Anwendung einzuführen. Hier ist ein Beispiel, um dies zu veranschaulichen.

Nehmen wir an, Sie bauen ein Geschäft und haben eine Ansicht, die für die Anzeige eines Produkts verantwortlich ist. Wenn das Produkt nicht vorhanden ist, leiten Sie zur Startseite weiter:

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})

Jetzt möchten Sie eine zweite Ansicht hinzufügen, um Kundenbewertungen für ein Produkt anzuzeigen. Es sollte auch für nicht vorhandene Produkte auf die Homepage umgeleitet werden. In einem ersten Schritt extrahieren Sie diese Funktionalität ausproduct_view() in eine Hilfsfunktionget_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})

Leider funktioniert die Weiterleitung nach dem Refactoring nicht mehr.

Weiterleitungen, die nicht aufhören, umzuleiten

Wenn Sie sich mit Weiterleitungen befassen, können Sie versehentlich eine Umleitungsschleife erstellen, indem URL A eine Umleitung zurückgibt, die auf URL B verweist, die eine Umleitung zu URL A zurückgibt, und so weiter. Die meisten HTTP-Clients erkennen diese Art von Umleitungsschleife und zeigen nach einer Reihe von Anforderungen eine Fehlermeldung an.

Leider kann es schwierig sein, diese Art von Fehler zu erkennen, da auf der Serverseite alles gut aussieht. Sofern sich Ihre Benutzer nicht über das Problem beschweren, besteht der einzige Hinweis darauf, dass möglicherweise etwas nicht stimmt, darin, dass Sie eine Reihe von Anfragen von einem Client erhalten haben, die alle zu einer Umleitungsantwort in schneller Folge führen, jedoch keine Antwort mit200 OK Status.

Hier ist ein einfaches Beispiel für eine Umleitungsschleife:

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

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

Dieses Beispiel veranschaulicht das Prinzip, ist jedoch zu einfach. Die Umleitungsschleifen, denen Sie im wirklichen Leben begegnen, werden wahrscheinlich schwerer zu erkennen sein. Schauen wir uns ein ausführlicheres Beispiel an:

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() ruft alle vorgestellten Produkte ab, dhProduct Instanzen, wobei.featured aufTrue gesetzt ist. Wenn nur ein Produkt vorhanden ist, wird es direkt zuproduct_view() umgeleitet. Andernfalls wird eine Vorlage mit dem Abfragesatzfeatured_productsgerendert.

Dasproduct_view kommt aus dem vorherigen Abschnitt bekannt vor, weist jedoch zwei geringfügige Unterschiede auf:

  • Die Ansicht versucht, einProduct abzurufen, das auf Lager ist, was dadurch angezeigt wird, dass.in_stock aufTrue gesetzt ist.

  • Die Ansicht leitet zufeatured_products_view() um, wenn kein Produkt auf Lager ist.

Diese Logik funktioniert so lange, bis Ihr Geschäft Opfer seines eigenen Erfolgs wird und das Produkt, das Sie derzeit haben, nicht mehr vorrätig ist. Wenn Sie.in_stock aufFalse setzen, aber vergessen,.featured auch aufFalse zu setzen, bleibt jeder Besucher Ihrerfeature_product_view() in einer Umleitungsschleife stecken .

Es gibt keine kugelsichere Möglichkeit, diese Art von Fehler zu verhindern. Ein guter Ausgangspunkt besteht jedoch darin, zu überprüfen, ob die Ansicht, auf die Sie umleiten, selbst Weiterleitungen verwendet.

Permanente Weiterleitungen sind permanent

Permanente Weiterleitungen können wie schlechte Tätowierungen sein: Sie scheinen zu dieser Zeit eine gute Idee zu sein, aber sobald Sie feststellen, dass sie ein Fehler waren, kann es ziemlich schwierig sein, sie loszuwerden.

Wenn ein Browser eine permanente Umleitungsantwort für eine URL erhält, wird diese Antwort auf unbestimmte Zeit zwischengespeichert. Jedes Mal, wenn Sie die alte URL in Zukunft anfordern, lädt der Browser sie nicht und lädt die neue URL direkt.

Es kann ziemlich schwierig sein, einen Browser davon zu überzeugen, eine URL zu laden, die einmal eine permanente Weiterleitung zurückgegeben hat. Google Chrome ist besonders aggressiv, wenn es um das Zwischenspeichern von Weiterleitungen geht.

Warum kann das ein Problem sein?

Stellen Sie sich vor, Sie möchten eine Webanwendung mit Django erstellen. Sie registrieren Ihre Domain untermyawesomedjangowebapp.com. Als ersten Schritt installieren Sie eine Blog-App beihttps://myawesomedjangowebapp.com/blog/, um eine Start-Mailingliste zu erstellen.

Die Homepage Ihrer Site beihttps://myawesomedjangowebapp.com/ befindet sich noch im Aufbau. Sie leiten also zuhttps://myawesomedjangowebapp.com/blog/ weiter. Sie entscheiden sich für eine permanente Weiterleitung, weil Sie gehört haben, dass permanente Weiterleitungen zwischengespeichert werden und das Zwischenspeichern die Dinge schneller macht, und schneller ist besser, weil Geschwindigkeit ein Faktor für das Ranking in den Google-Suchergebnissen ist.

Wie sich herausstellt, sind Sie nicht nur ein großartiger Entwickler, sondern auch ein talentierter Schriftsteller. Ihr Blog wird populär und Ihre Start-Mailingliste wächst. Nach ein paar Monaten ist Ihre App fertig. Es hat jetzt eine glänzende Homepage und Sie entfernen schließlich die Weiterleitung.

Sie senden eine Ankündigungs-E-Mail mit einem speziellen Rabattcode an Ihre umfangreiche Start-Mailingliste. Sie lehnen sich zurück und warten, bis die Anmeldungsbenachrichtigungen eingehen.

Zu Ihrem Entsetzen füllt sich Ihre Mailbox mit Nachrichten von verwirrten Besuchern, die Ihre App besuchen möchten, aber immer zu Ihrem Blog weitergeleitet werden.

Was ist passiert? Ihre Blog-Leser hattenhttps://myawesomedjangowebapp.com/ besucht, als die Weiterleitung zuhttps://myawesomedjangowebapp.com/blog/ noch aktiv war. Da es sich um eine permanente Weiterleitung handelte, wurde sie in ihren Browsern zwischengespeichert.

Wenn sie auf den Link in Ihrer Mail mit der Ankündigung des Starts geklickt haben, haben sich ihre Browser nie die Mühe gemacht, Ihre neue Homepage zu überprüfen, und sind direkt zu Ihrem Blog gegangen. Anstatt Ihren erfolgreichen Start zu feiern, weisen Sie Ihre Benutzer an, mitchrome://net-internals herumzuspielen, um den Cache ihrer Browser zurückzusetzen.

Die permanente Natur permanenter Weiterleitungen kann Sie auch bei der Entwicklung auf Ihrem lokalen Computer beißen. Lassen Sie uns zu dem Moment zurückspulen, als Sie diese schicksalhafte permanente Weiterleitung für myawesomedjangowebapp.com implementiert haben.

Sie starten den Entwicklungsserver und öffnenhttp://127.0.0.1:8000/. Wie beabsichtigt leitet Ihre App Ihren Browser zuhttp://127.0.0.1:8000/blog/ um. Mit Ihrer Arbeit zufrieden, stoppen Sie den Entwicklungsserver und gehen zum Mittagessen.

Sie kehren mit vollem Bauch zurück und sind bereit, einige Kundenarbeiten in Angriff zu nehmen. Der Client möchte einige einfache Änderungen an seiner Homepage vornehmen. Laden Sie daher das Projekt des Clients und starten Sie den Entwicklungsserver.

Aber warte, was ist hier los? Die Homepage ist kaputt, es wird jetzt ein 404 zurückgegeben! Aufgrund des Einbruchs am Nachmittag dauert es eine Weile, bis Sie feststellen, dass Sie zuhttp://127.0.0.1:8000/blog/ umgeleitet werden, was im Projekt des Kunden nicht vorhanden ist.

Für den Browser spielt es keine Rolle, dass die URLhttp://127.0.0.1:8000/jetzt einer völlig anderen Anwendung dient. Für den Browser ist lediglich wichtig, dass diese URL in der Vergangenheit eine permanente Weiterleitung anhttp://127.0.0.1:8000/blog/ zurückgegeben hat.

Aus dieser Geschichte geht hervor, dass Sie nur permanente Weiterleitungen für URLs verwenden sollten, die Sie nie wieder verwenden möchten. Es gibt einen Platz für permanente Weiterleitungen, aber Sie müssen sich ihrer Konsequenzen bewusst sein.

Selbst wenn Sie sicher sind, dass Sie wirklich eine permanente Weiterleitung benötigen, ist es eine gute Idee, zuerst eine temporäre Weiterleitung zu implementieren und erst dann zu ihrem permanenten Cousin zu wechseln, wenn Sie zu 100{}icher sind, dass alles wie beabsichtigt funktioniert.

Nicht validierte Weiterleitungen können die Sicherheit beeinträchtigen

Aus Sicherheitsgründen sind Weiterleitungen eine relativ sichere Technik. Ein Angreifer kann eine Website mit einer Weiterleitung nicht hacken. Schließlich leitet eine Weiterleitung nur zu einer URL weiter, die ein Angreifer einfach in die Adressleiste seines Browsers eingeben kann.

Wenn Sie jedoch eine Art Benutzereingabe wie einen URL-Parameter ohne ordnungsgemäße Validierung als Umleitungs-URL verwenden, kann dies von einem Angreifer für einen Phishing-Angriff missbraucht werden. Diese Art der Umleitung wird alsopen or unvalidated redirect bezeichnet.

Es gibt legitime Anwendungsfälle für die Umleitung zu einer URL, die aus Benutzereingaben gelesen wird. Ein Paradebeispiel ist die Anmeldeansicht von Django. Es akzeptiert einen URL-Parameternext, der die URL der Seite enthält, zu der der Benutzer nach der Anmeldung umgeleitet wird. Um den Benutzer nach der Anmeldung zu seinem Profil umzuleiten, sieht die URL möglicherweise folgendermaßen aus:

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

Django validiert zwar den Parameternext, aber nehmen wir für eine Sekunde an, dass dies nicht der Fall ist.

Ohne Validierung könnte ein Angreifer eine URL erstellen, die den Benutzer zu einer von ihm kontrollierten Website weiterleitet, zum Beispiel:

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

Die Websitemyawesomedjangowebapp.co zeigt dann möglicherweise eine Fehlermeldung an und bringt den Benutzer dazu, seine Anmeldeinformationen erneut einzugeben.

Der beste Weg, um offene Weiterleitungen zu vermeiden, besteht darin, beim Erstellen einer Weiterleitungs-URL keine Benutzereingaben zu verwenden.

Wenn Sie nicht sicher sind, ob eine URL für die Umleitung sicher ist, können Sie sie mit der Funktiondjango.utils.http.is_safe_url() validieren. Der Docstring erklärt seine Verwendung recht gut:

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

Geben SieTrue zurück, wenn die URL eine sichere Umleitung ist (d. H. Es verweist nicht auf einen anderen Host und verwendet ein sicheres Schema. Geben Sie immerFalse für eine leere URL zurück. Wennrequire_httpsTrue ist, wird nur "https" als gültiges Schema betrachtet, im Gegensatz zu "http" und "https" mit dem StandardwertFalse. (Source)

Schauen wir uns einige Beispiele an.

Eine relative URL gilt als sicher:

>>>

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

Eine URL, die auf einen anderen Host verweist, wird im Allgemeinen nicht als sicher angesehen:

>>>

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

Eine URL, die auf einen anderen Host verweist, gilt als sicher, wenn ihr Host inallowed_hosts angegeben wird:

>>>

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

Wenn das Argumentrequire_httpsTrue ist, wird eine URL, die das Schemahttpverwendet, nicht als sicher angesehen:

>>>

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

Zusammenfassung

Dies schließt diese Anleitung zu HTTP-Weiterleitungen mit Django ab. Herzlichen Glückwunsch: Sie haben jetzt alle Aspekte von Weiterleitungen angesprochen, von den Details des HTTP-Protokolls auf niedriger Ebene bis zur Behandlung auf hoher Ebene in Django.

Sie haben erfahren, wie eine HTTP-Umleitung unter der Haube aussieht, welche unterschiedlichen Statuscodes es gibt und wie sich permanente und temporäre Weiterleitungen unterscheiden. Dieses Wissen ist nicht spezifisch für Django und für die Webentwicklung in jeder Sprache wertvoll.

Sie können jetzt eine Umleitung mit Django durchführen, entweder mithilfe der UmleitungsantwortklassenHttpResponseRedirect undHttpResponsePermanentRedirect oder mit der Komfortfunktiondjango.shortcuts.redirect(). Sie haben Lösungen für einige fortgeschrittene Anwendungsfälle gesehen und wissen, wie Sie häufige Fallstricke vermeiden können.

Wenn Sie weitere Fragen zu HTTP-Weiterleitungen haben, hinterlassen Sie unten einen Kommentar und freuen Sie sich in der Zwischenzeit über die Weiterleitung!