Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Gibt es effizientere Alternativen zu anonymen ASP.NET 2.0-Profilen?

Ich habe die Problemumgehung implementiert, die ich in meinem ursprünglichen Beitrag gefunden habe, es stellte sich jedoch heraus, dass sie etwas anders war als das, was ich ursprünglich beschrieben hatte. Der Fix kann eigentlich in 2 Teile aufgeteilt werden - einen, um das Problem zu beheben, dass die Datenbank aktualisiert wird, wenn Cookies deaktiviert sind, und den zweiten, um zu erkennen, wenn Cookies deaktiviert sind, ohne eine Umleitung durchzuführen.

Ich habe bereits den Lösung für anonyme Profile, die Aufzeichnungen erstellen, wenn Cookies deaktiviert sind .

Also konzentriere ich mich jetzt auf den zweiten Teil – das Abrufen von Informationen in das Profil von der ersten angeforderten Seite. Dies ist nur erforderlich, wenn Sie Analytics-Tracking oder ähnliches durchführen - der erste Teil kümmert sich darum, die Datenbank davor zu schützen, dass sie mit völlig nutzlosen Daten gefüllt wird, wenn 1) Cookies deaktiviert sind und 2) anonyme Profileigenschaften verwendet werden und funktionieren ab der zweiten Anfrage (oder dem ersten Postback).

Als ich das Problem der Überprüfung untersuchte, um festzustellen, ob Cookies aktiviert sind, verwendeten die meisten Lösungen eine Weiterleitung entweder auf dieselbe Seite oder eine andere Seite und wieder zurück. Interessanterweise MSDN war derjenige, der sich die 2-Umleitungslösung ausgedacht hat.

Während unter bestimmten Umständen eine Umleitung akzeptabel ist, wollte ich nicht, dass die zusätzliche Auswirkung auf die Leistung die Mehrheit unserer Benutzer beeinträchtigt. Stattdessen habe ich mich für einen anderen Ansatz entschieden:Verwenden Sie AJAX, um Code auf dem Server auszuführen, nachdem die erste Anfrage abgeschlossen wurde. Dies hat zwar den Vorteil, dass es keine Umleitung verursacht, hat aber den Nachteil, dass es nicht funktioniert, wenn JavaScript deaktiviert ist. Ich habe mich jedoch für diesen Ansatz entschieden, da der Prozentsatz der Daten, die bei der ersten Anfrage verloren gehen, unbedeutend ist und die Anwendung selbst nicht von diesen Daten abhängt.

Gehen Sie also vom Anfang des Prozesses bis zum Ende durch ...

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

    If Not Me.IsPostBack Then

        If Session.IsNewSession Then
            Me.InjectProfileJavaScript()
        ElseIf AnonymousProfile.IsAnonymousCookieStored Then
            'If cookies are supported, and this isn't the first request, update the
            'profile using the current page data.
            UpdateProfile(Request.RawUrl, Request.UrlReferrer.OriginalString, CurrentProductID.ToString)
        End If

    End If

End Sub

Dies ist die Page_Load-Methode, die in meiner benutzerdefinierten PageBase-Klasse platziert ist, von der alle Seiten im Projekt erben. Als Erstes überprüfen wir, ob es sich um eine neue Sitzung handelt, indem wir die Eigenschaft Session.IsNewSession überprüfen. Diese Eigenschaft ist immer wahr, wenn Cookies deaktiviert sind oder wenn dies die erste Anfrage ist. In beiden Fällen wollen wir nicht in die Datenbank schreiben.

Der „else if“-Abschnitt wird ausgeführt, wenn der Client das Sitzungscookie akzeptiert hat und dies nicht die erste Anfrage an den Server ist. Beachten Sie bei diesem Code-Snippet, dass beide Abschnitte nicht in derselben Anfrage ausgeführt werden können, was bedeutet, dass das Profil nur 1 (oder 0) Mal pro Anfrage aktualisiert werden kann.

Die AnonymousProfile-Klasse ist in meinen andere Beiträge erstellen .

Private Sub InjectProfileJavaScript()

    Dim sb As New StringBuilder

    sb.AppendLine("$(document).ready(function() {")
    sb.AppendLine("  if (areCookiesSupported() == true) {")
    sb.AppendLine("    $.ajax({")
    sb.AppendLine("      type: 'POST',")
    sb.AppendLine("      url: 'HttpHandlers/UpdateProfile.ashx',")
    sb.AppendLine("      contentType: 'application/json; charset=utf-8',")
    sb.AppendFormat("      data: ""{3}'RawUrl':'{0}', 'ReferralUrl':'{1}', 'ProductID':{2}{4}"",", Request.RawUrl, Request.UrlReferrer, CurrentProductID.ToString, "{", "}")
    sb.AppendLine()
    sb.AppendLine("      dataType: 'json'")
    sb.AppendLine("    });")
    sb.AppendLine("  }")
    sb.AppendLine("});")

    Page.ClientScript.RegisterClientScriptBlock(GetType(Page), "UpdateProfile", sb.ToString, True)

End Sub

Public Shared Sub UpdateProfile(ByVal RawUrl As String, ByVal ReferralUrl As String, ByVal ProductID As Integer)
    Dim context As HttpContext = HttpContext.Current
    Dim profile As ProfileCommon = CType(context.Profile, ProfileCommon)

    Dim CurrentUrl As New System.Uri("http://www.test.com" & RawUrl)
    Dim query As NameValueCollection = HttpUtility.ParseQueryString(CurrentUrl.Query)
    Dim source As String = query.Item("source")
    Dim search As String = query.Item("search")
    Dim OVKEY As String = query.Item("OVKEY")

    'Update the profile
    profile.TestValue1 = source
    profile.TestValue2 = search

End Sub

Als nächstes haben wir unsere Methode, um einen AJAX-Aufruf in die Seite einzufügen. Denken Sie daran, dass dies immer noch die Basisklasse ist, sodass unabhängig von der Seite, auf der der Benutzer landet, dieser Code bei der ersten Seitenanforderung ausgeführt wird.

Innerhalb des JavaScripts testen wir zunächst, ob Cookies aktiviert sind, und rufen in diesem Fall mithilfe von AJAX und JQuery einen benutzerdefinierten Handler auf dem Server auf. Wir übergeben die Parameter vom Server an diesen Code (obwohl 2 davon auch nur vom Client geliefert werden könnten, sind die zusätzlichen Bytes nicht so wichtig).

Die zweite Methode aktualisiert das Profil und enthält meine benutzerdefinierte Logik dafür. Ich habe einen Ausschnitt zum Analysieren der Abfragezeichenfolgenwerte aus einer Teil-URL eingefügt. Aber das einzige, was hier wirklich bekannt sein muss, ist, dass dies die gemeinsame Methode ist, die das Profil aktualisiert.

Wichtig: Damit der AJAX-Aufruf funktioniert, muss der folgende Handler zum Abschnitt system.web der Datei web.config hinzugefügt werden:

<httpModules>
    <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>

Ich entschied, dass es am besten wäre, den Client auf Cookies zu testen und den zusätzlichen AJAX-Aufruf nicht durchzuführen, wenn Cookies deaktiviert sind. Verwenden Sie zum Testen der Cookies diesen Code:

function areCookiesSupported() {
    var c='c';var ret = false;
    document.cookie = 'c=2;';
    if (document.cookie.indexOf(c,0) > -1) {
        ret = true;
    } else {
        ret = false;
    }
    deleteCookie(c);
    return ret
}
function deleteCookie(name) {
    var d = new Date();
    document.cookie = name + '=1;expires=' + d.toGMTString() + ';' + ';';
}

Dies sind 2 JavaScript-Funktionen (in einer benutzerdefinierten .js-Datei), die einfach ein Cookie schreiben und es zurücklesen, um festzustellen, ob Cookies gelesen werden können. Anschließend wird das Cookie bereinigt, indem ein Ablaufdatum in der Vergangenheit festgelegt wird.

<%@ WebHandler Language="VB" Class="Handlers.UpdateProfile" %>

Imports System
Imports System.Web
Imports System.Web.SessionState
Imports Newtonsoft.Json
Imports System.Collections.Generic
Imports System.IO

Namespace Handlers

    Public Class UpdateProfile : Implements IHttpHandler : Implements IRequiresSessionState

        Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

            If AnonymousProfile.IsAnonymousCookieStored Then

                If context.Session.IsNewSession Then
                    'Writing to session state will reset the IsNewSession flag on the
                    'next request. This will fix a problem if there is no Session_Start
                    'defined in global.asax and no other session variables are written.
                    context.Session("ActivateSession") = ""
                End If

                Dim reader As New StreamReader(context.Request.InputStream)
                Dim params As Dictionary(Of String, String) = JsonConvert.DeserializeObject(Of Dictionary(Of String, String))(reader.ReadToEnd())

                Dim RawUrl As String = params("RawUrl")
                Dim ReferralUrl As String = params("ReferralUrl")
                Dim ProductID As Integer = params("ProductID")

                PageBase.UpdateProfile(RawUrl, ReferralUrl, ProductID)
            End If
        End Sub

        Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
            Get
                Return False
            End Get
        End Property

    End Class

End Namespace

Dies ist unsere benutzerdefinierte HttpHandler-Klasse, die die AJAX-Anforderung empfängt. Die Anfrage wird nur verarbeitet, wenn das .ASPXANONYMOUS-Cookie übergeben wird (erneut überprüft durch Verwendung der AnonymousProfile-Klasse aus meinem anderen Beitrag), wodurch verhindert wird, dass Robots und andere Skripte es ausführen.

Als Nächstes führen wir Code aus, um das Sitzungsobjekt zu aktualisieren, falls dies erforderlich ist. Aus irgendeinem seltsamen Grund bleibt der IsNewSession-Wert wahr, bis die Sitzung tatsächlich aktualisiert wird, aber nur, wenn kein Handler für Session_Start in Global.asax vorhanden ist. Damit dieser Code sowohl mit als auch ohne eine Global.asax-Datei und ohne anderen Code, der das Sitzungsobjekt aktualisiert, funktioniert, führen wir hier ein Update durch.

Das nächste Stück Code, das ich mir von dieser Beitrag und enthält eine Abhängigkeit zum JSON.NET-Serializer. Ich war wegen der zusätzlichen Abhängigkeit hin und her gerissen, diesen Ansatz zu verwenden, entschied aber letztendlich, dass der JSON-Serializer in Zukunft wahrscheinlich wertvoll sein wird, wenn ich weiterhin AJAX und JQuery zur Website hinzufüge.

Dann holen wir uns einfach die Parameter und übergeben sie an unsere gemeinsame UpdateProfile-Methode in der PageBase-Klasse, die zuvor definiert wurde.

<!-- Required for anonymous profiles -->
<anonymousIdentification enabled="true"/>
<profile defaultProvider="SqlProvider" inherits="AnonymousProfile">
    <providers>
        <clear/>
        <add name="SqlProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="SqlServices" applicationName="MyApp" description="SqlProfileProvider for profile test web site"/>
    </providers>
    <properties>
        <add name="TestValue1" allowAnonymous="true"/>
        <add name="TestValue2" allowAnonymous="true"/>
    </properties>
</profile>

Schließlich haben wir noch unseren Konfigurationsabschnitt für die Profileigenschaften, der so eingerichtet ist, dass er anonym verwendet werden kann (ich habe absichtlich den Abschnitt für die Verbindungszeichenfolge weggelassen, aber eine entsprechende Verbindungszeichenfolge und eine Datenbank sind ebenfalls erforderlich). Das Wichtigste, was hier zu beachten ist, ist die Einbeziehung des Attributs inherits in das Profil. Dies gilt wiederum für die AnonymousProfile-Klasse, die in meinem andere Posts .