Session State bei parallelen Browser-Frames und Fenstern
Geschrieben von: Christian Koller Das Session Objekt und der Session State in ASP sind vielfältig einsetzbar. Sei es nun zur Absicherung eines Administrationsbereiches einer Website, zur Identifikation von Benützern allgemein, zum Personalisieren von Sites oder einfach nur zum Speichern von Userbezogenen Daten und Ergebnissen für die Dauer eines Websitebesuches. Wie manch einer schon mit Bedauern feststellen mußte, funktioniert eine Session nur dann sehr gut, wenn der User von einer einfachen Seite zur nächsten Seite geht. Arbeitet man jedoch mit Framesets, so taucht ein Problem auf. Wird in einer ASP-Seite, die Bestandteil eines Framesets ist, der Session Variablen ein Wert zugewiesen, so ist dieser Wert in einer anderen ASP-Seite des Framesets nicht auslesbar. Was ist der Grund für dieses Verhalten? Das Session Objekt identifiziert den Besucher, wie im Artikel Session Variablen - Verwendung und Stolpersteine beschrieben, über einen temporären Cookie, der während des Besuches der Website vom Browser gespeichert wird. Viele Browser, wie Microsoft Internet Explorer 4.x und 5.0, speichern einen temporären Cookie (wie den Session Cookie) im Speicher des Prozesses, der die Webseite gerade anzeigt. Bei einem Frameset wird jeder Frame von einem eigenen Prozeß verwaltet und daher hat auch jeder Frame seine eigenen temporären Cookies. So schickt auch der Webserver beim Aufruf mehrerer ASP Seiten im Frameset zu jeder ASP Seite einen eigenen Session Cookie mit und generiert für jede ASP Seite ein eigenes Session Objekt. Das ist der Grund dafür, daß man von einer ASP Seite des Framesets keinen Zugriff auf Session Variablen einer anderen ASP Seite des Framesets mehr hat. (Vergleiche auch MSDN Artikel "Session Variables Lost When ASP is Located in Frameset") Noch komplexer wird die Problematik, wenn man unterschiedliche Frame-Seiten nicht nacheinander aufruft, sondern quasi gleichzeitig. Dies ist im speziellen der Fall, wenn das Frameset das erste Mal geladen wird. Es ist möglich, daß zuerst die ASP-Seite Nr. 1 geladen wird und dann die ASP-Seite Nr. 2. Auch der umgekehrte Fall ist möglich. Es ist sogar wahrscheinlich, daß der Browser beide Seiten parallel vom Webserver lädt. Wie läßt sich nun dieses Problem lösen? Folgende Ansätze bieten sich an, um Session Informationen für alle ASP-Seiten im Frameset verfügbar zu machen:
1. Werte in Cookies anstatt in Session Variablen speichernDabei speichert man keine Werte in Session Variablen, sondern direkt in Cookies, die ein definiertes "Ablaufdatum" (engl. Expiration Date) haben. Da "permanente" Cookies an einem zentralen Ort auf der Festplatte des Benutzers gespeichert werden, stehen die Cookies und deren Werte für alle ASP-Seiten der selben Website und damit auch für die ASP Seiten innerhalb eines Framesets zur Verfügung. Wenn man sichergehen möchte, daß die Cookies bereits vor dem Laden der einzelnen Frames gesetzt sind und somit allen ASP-Dateien im Frameset zur Verfügung stehen, ist es ratsam, die Cookies bereits in der Frameset-Hauptseite zu setzen. Die Frameset-Hauptseite muß dazu natürlich auch eine ASP-Seite sein. Wenn man also bisher einen Wert in einer Session Variablen gespeichert hat, so setzt man dazu nun einen Wert für einen Cookie. Vorher: Zuweisen eines Wertes zu einer Session Variablen.
Session("VariablenName") = Wert Jetzt: Setzen eines Wertes für einen permanenten Cookie (mit Ablaufdatum).
Response.Cookies("VariablenName") = Wert ' ### Ablaufdatum auf 24 Stunden von jetzt setzen: Response.Cookies("VariablenName").Expires = Now() + 1 Achtung: Da der IIS den Cookie als HTTP Header zum Browser schickt muß dieser Befehl ausgeführt werden, bevor ein normaler Webseiteninhalt zum Browser übertragen wird. Dafür gibt es 2 Möglichkeiten:
Anmerkung: Natürlich lassen sich in Cookies keine Objekte (wie Recordsets, Connections oder Dictionary Objekte) speichern. Wie liest man nun den Wert des Cookies wieder aus? Bisher: Auslesen eines Wertes aus einer Session Variablen.
Wert = Session("VariablenName") Stattdessen: Das Auslesen eines Cookie-Wertes.
Wert = Request.Cookies("VariablenName") Vorteile dieser Methode: Wenig Programmieraufwand. Man kann bestehende ASP-Seiten einfach umschreiben. Nachteile: Man kann einen Cookie-Wert nur dann an beliebiger
Stelle im Script setzen, wenn man den Output Buffer des Webservers
für die ASP-Seite aktiviert hat. 2. Simulieren eines Session States mit QueryStringsDa manche Benutzer immer noch Abneigungen gegen Cookies haben, bleibt oftmals nur die Möglichkeit, für einen Benutzer während seines Besuches der Website eine Identifikationsnummer (ähnlich wie ein Session Cookie) im QueryString der URL mitzuführen. Man kann dann Werte für diesen Benutzer für die Dauer seines Besuches in einer Datenbank oder in Application Variablen speichern und anhand der Identifikationsnummer eindeutig dem Benutzer (und seiner Session) zuordnen. Wie funktioniert nun der Trick mit dem QueryString genau? Ein QueryString ist nichts anderes als eine Zeichenkette, die mit einem Fragezeichen (?) beginnt und an die URL (Adresse der Webseite) angehängt wird. Der QueryString enthält dann ein oder mehrere Paare von Namen und Werten. Für unser spezielles Problem ist es zielführend im QueryString die Identifikationsnummer für den Besucher speichern. Eine URL mit QueryString (fett) könnte also folgendermaßen aussehen:
http://www.aspheute.com/Seite.asp?IdNr=12345 Wenn der Besucher das erste Mal auf eine Seite der Website
kommt, so hat er noch keinen (oder möglicherweise einen
alten) QueryString in der aufrufenden URL.
In der ASP Seite selbst sind folgende Dinge wichtig:
Weiters ist zu beachten: Der Mechanismus, der für das "Zuweisen" und "Lesen" der Variablen zuständig ist, muß effizient programmiert sein um den Webserver nicht zu sehr zu belasten. Wenn auf Werte eines bestimmten Benutzers eine definierte Zeitdauer lang nicht mehr zugegriffen wurde, so sind alle gespeicherten Werte der Benutzer Session zu löschen. Ansonsten würde die Datenbank (oder das Application Objekt) in einer riesigen, speicherfressenden Sammlung von nicht mehr benötigten Daten enden. Beispiel für den QueryString MechanismusIm folgenden skizziere ich die Implementation des QueryString Mechanismus, wie also eine Benutzer ID generiert und von Seite zu Seite weitergegeben wird. Einfache ASP SeiteDie folgende Beispielseite, die auch Teil eines Framesets sein kann, liest die Benutzer ID aus dem QueryString, speichert den Wert in einer Variablen und hängt dann die Benutzer ID an jede auf der Seite vorkommende URL im QueryString an.
<% SessionIdNr = Request.QueryString("IdNr") If SessionIdNr = "" Then ' ### Keine Id im QueryString, ' ### generiere Id SessionIdNr = Session.SessionID End If %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title></title> </head> <body> <!-- Textlink --> Klicken Sie <a href="seite1.asp?IdNr=<%= SessionIdNr %>">hier</a> <br> <!-- Textlink, der bereits Werte im QueryString hat --> <a href="seite2.asp?Name=Wert&IdNr=<%= SessionIdNr %>"> Neue Seite</a> <br> <!-- Link bei einem Image Tag --> <a href="seite3.asp?IdNr=<%= SessionIdNr %>"> <img src="bild.png" border=0 alt=""></a> <!-- Formular mit POST --> <form action="seite5.asp?IdNr=<%= SessionIdNr %>" method="POST"> <input type="Text" name="Name"><br> <input type="Submit" name="Submit" value="Absenden"> </form> <br> <!-- Formular mit GET --> <form action="seite6.asp" method="GET"> <input type="Hidden" name="IdNr" value="<%= SessionIdNr %>"> <input type="Text" name="Name"><br> <input type="Submit" name="Submit" value="Absenden"> </form> <br> <!-- Link in einem JavaScript Event --> <a onClick="window.open('seite4.asp?IdNr=<%= SessionIdNr %>');"> Neues Fenster</a> </body> </html> Das Skript beinhaltet die folgenden Operationen: Die IdNr aus dem QueryString wird mit Hilfe der QueryString Collection des Request Objektes ausgelesen. Wenn Sie eine neue Id generieren, so können Sie den Wert aus Session.SessionID nur dann verwenden, wenn sie die Werte für die Session des Benutzers im Application Object speichern. Speichern Sie allerdings die Session Werte in einer Datenbank, so sollten Sie die eindeutige Id von der Datenbank selbst generieren lassen. Microsoft SQL Server 7.0 oder Access 2000 Datenbanken stellen zu diesem Zweck die Identity bzw. Autonumber Felder zur Verfügung (Siehe auch Artikel Zugriff auf autom. generierte ID beim Einfügen eines Datensatzes). Sonst kann es passieren, wenn der Webserver kurzzeitig ausfällt oder die Webserver Applikation einen Neustart durchführt, daß eine SessionID generiert wird, die bereits von einem anderen Benutzer gehalten wird. Damit ist die "Id" nicht mehr eindeutig und das Chaos wäre perfekt. In gewöhnlichen Links wird einfach der Wert aus der Variablen SessionIdNr im QueryString unter dem Namen IdNr angehängt. In Formularen, die mit POST geschickt werden, wird der Wert der Variablen SessionIdNr im QueryString der Empfänger URL (Parameter Action im <FORM> Tag) unter dem Namen IdNr angehängt. Bei Formularen, die mittels GET zum Webserver gesendet werden, ist es nötig, ein verborgenes Feld (engl. Hidden Field) in das Formular (zwischen <FORM> und </FORM>) einzufügen. Dieses Feld bewirkt, daß der Name und der Wert des Feldes zum Webserver geschickt werden:
<input type="Hidden" name="IdNr" value="<%= SessionIdNr %>"> Was dieses Beispiel nicht zeigt ist, wie man nun, anhand dieser Benutzer Id, Zugriff auf Werte der Benutzer Session erhält. Die von Seite zu Seite weitergegebene Benutzer Id (IdNr im QueryString, SessionIdNr im ASP Skript) identifiziert den Benutzer eindeutig. Anhand dieser eindeutigen Id kann man nun Werte in einer Datenbank oder in der Application Variablen speichern und wieder auslesen. Die genaue Beschreibung der Implementation solch eines Algorithmus in ASP würde allerdings den Rahmen dieses Artikels sprengen. Insbesondere ist darauf zu achten, daß Daten von inaktiven Benutzer-Sessions nach einer bestimmten Zeit gelöscht werden um Speicherplatz fressenden Datenmüll zu vermeiden. FramesetUm in einem Frameset die Benutzer ID an die Unter-Frames weiterzugeben, fügt man in die Benutzer ID nach bekanntem Muster in alle QueryStrings der URL (in den SRC Parametern der <Frame> Tags) ein:
<% SessionIdNr = Request.QueryString("IdNr") If SessionIdNr = "" Then ' ### Keine Id im QueryString, ' ### generiere Id SessionIdNr = Session.SessionID End If %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Frameset</title> </head> <!-- frames --> <frameset rows="50%,*"> <frame name="Frame 1" src="frame1.asp?IdNr=<%= SessionIdNr %>"> <frame name="Frame 2" src="frame2.asp?IdNr=<%= SessionIdNr %>"> </frameset> </html> Die Vorteile des QueryString Mechanismus: Der simulierte Session State bleibt nur für die Dauer des Besuches erhalten. Es werden keine Cookies gesetzt. Nachteile: Der Programmieraufwand ist sehr groß. Zum einen muß an jeden Link (ob Textlink, Link hinter einem Bild, JavaScript Link, oder Link in einem Formular) dynamisch ein QueryString angehängt werden. Daher müßen alle Seiten, die Links (oder Formulare) enthalten, ASP Seiten sein, ansonsten lassen sich die im QueryString gespeicherten Werte nicht dynamisch auslesen und in Links einfügen. Weiters muß man für das Management der Benutzersessions (und der während der Session gespeicherten Benutzer Werte) die Funktionalität selber implementieren. Dies erfordert, je nach Ansatz, fundiertes Wissen über das Application Objekt, über Datenbanken oder andere Möglichkeiten, Daten so zu speichern, daß der Webserver unter Angabe der Benutzer ID und des Variablennamens einen Zugriff auf den zugehörigen Wert der Benutzer Session hat. SchlußbemerkungDer Verzicht auf den Einsatz des Session Objektes in Webprojekten mit Frames ist ein immer wieder auftauchendes und ernstes Problem. Die Möglichkeiten, diese Browserunzulänglichkeiten zu umgehen, sind vielfältig. In diesem Artikel wurden das Speichern von Session Werten als Cookie Werte sowie das Mitführen einer Benutzer Id im QueryString aller URLs der Website dargestellt. Download des CodesKlicken Sie hier, um den Download zu starten. Verwandte Artikel
Redirects mit Frame-Targets Links zu anderen Sites
Session Variables Lost When ASP is Located in Frameset Wenn Sie jetzt Fragen haben...Wenn Sie Fragen rund um die in diesem Artikel vorgestellte Technologie haben, dann schauen Sie einfach bei uns in den Community Foren der deutschen .NET Community vorbei. Die Teilnehmer helfen Ihnen gerne, wenn Sie sich zur im Artikel vorgestellten Technologie weiterbilden möchten. Haben Sie Fragen die sich direkt auf den Inhalt des Artikels beziehen, dann schreiben Sie dem Autor! Unsere Autoren freuen sich über Feedback zu ihren Artikeln. Ein einfacher Klick auf die Autor kontaktieren Schaltfläche (weiter unten) und schon haben Sie ein für diesen Artikel personalisiertes Anfrageformular.
Und zu guter Letzt möchten wir Sie bitten, den Artikel zu bewerten. Damit helfen Sie uns, die Qualität der Artikel zu verbessern - und anderen Lesern bei der Auswahl der Artikel, die sie lesen sollten.
©2000-2006 AspHeute.com |