Geschrieben von: Christian Koller
Kategorie: ASP Tricks
This printed page brought to you by AlphaSierraPapa
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:
Dabei 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.
Die Werte der Cookies bleiben auch gespeichert,
wenn der Benutzer alle Browserfenster geschlossen hat.
Daher dauert die Session nicht 20 Minuten bzw. bis zum
Schließen aller Browserfenster, sondern bis
zum Ablauf des Gültigkeitsdatums des Cookies.
Da 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.
Daher müssen beim Aufruf einer beliebigen ASP-Seite
immer folgende Schritte gesetzt werden:
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.
Im folgenden skizziere ich die Implementation des QueryString Mechanismus, wie also eine Benutzer ID generiert und von Seite zu Seite weitergegeben wird.
Die 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.
Um 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.
Der 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.
This printed page brought to you by AlphaSierraPapa
Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20000830.zip
Redirects mit Frame-Targets
http:/www.aspheute.com/artikel/20010530.htm
Session Variablen - Verwendung und Stolpersteine
http:/www.aspheute.com/artikel/20000505.htm
Zugriff auf autom. generierte ID beim Einfügen eines Datensatzes
http:/www.aspheute.com/artikel/20000606.htm
Session Variables Lost When ASP is Located in Frameset
http://support.microsoft.com/support/kb/articles/q178/0/37.asp
©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.