Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Esistono alternative più efficienti ai profili anonimi ASP.NET 2.0?

Ho implementato la soluzione alternativa che ho escogitato nel mio post originale, tuttavia si è rivelato un po' diverso da quello che avevo originariamente descritto. La correzione può effettivamente essere suddivisa in 2 parti:una per risolvere il problema con l'aggiornamento del database quando i cookie sono disabilitati e la seconda per rilevare quando i cookie sono disabilitati senza eseguire un reindirizzamento.

Ho già pubblicato il soluzione ai profili anonimi che creano record quando i cookie sono disabilitati .

Quindi ora mi concentrerò sulla seconda parte:ottenere informazioni nel profilo dalla prima pagina richiesta. Questo deve essere fatto solo se stai eseguendo il monitoraggio di analisi o qualcosa di simile:la prima parte si occuperà di proteggere il database dal riempirsi di dati totalmente inutili quando 1) i cookie sono disabilitati e 2) le proprietà del profilo anonime vengono utilizzate e funzionano da dalla seconda richiesta (o dal primo postback) in poi.

Quando ho studiato il problema del controllo per vedere se i cookie sono abilitati, la maggior parte delle soluzioni ha utilizzato un reindirizzamento alla stessa pagina o a una pagina diversa e viceversa. È interessante notare che MSDN è stato colui che ha escogitato la soluzione di reindirizzamento 2.

Sebbene in determinate circostanze un reindirizzamento sia accettabile, non volevo che l'impatto aggiuntivo sulle prestazioni influisse sulla maggior parte dei nostri utenti. Invece, ho optato per un altro approccio:utilizzare AJAX per eseguire codice sul server dopo che la prima richiesta è stata completata. Sebbene ciò abbia il vantaggio di non causare un reindirizzamento, ha lo svantaggio di non funzionare quando JavaScript è disabilitato. Tuttavia, ho optato per questo approccio perché la percentuale di dati che viene persa alla richiesta iniziale è insignificante e l'applicazione stessa non dipende da questi dati.

Quindi, dall'inizio alla fine del processo...

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

Questo è il metodo Page_Load inserito nella mia classe PageBase personalizzata, da cui ereditano tutte le pagine del progetto. La prima cosa che controlliamo è se si tratta di una nuova sessione controllando la proprietà Session.IsNewSession. Questa proprietà è sempre vera se i cookie sono disabilitati o se questa è la prima richiesta. In entrambi i casi, non vogliamo scrivere nel database.

La sezione "else if" viene eseguita se il client ha accettato il cookie di sessione e questa non è la prima richiesta al server. La cosa da notare su questo frammento di codice è che entrambe le sezioni non possono essere eseguite nella stessa richiesta, il che significa che il profilo può essere aggiornato solo 1 (o 0) volte per richiesta.

La classe AnonymousProfile è inclusa nel mio altro post .

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

Successivamente, abbiamo il nostro metodo per iniettare una chiamata AJAX nella pagina. Tieni presente che questa è ancora la classe base, quindi indipendentemente dalla pagina su cui l'utente atterra su questo codice verrà eseguita alla richiesta della prima pagina.

All'interno di JavaScript, testiamo prima se i cookie sono abilitati e, in tal caso, chiamiamo un gestore personalizzato sul server utilizzando AJAX e JQuery. Stiamo passando i parametri dal server a questo codice (sebbene 2 di essi potrebbero essere stati appena forniti dal client, i byte extra non sono così significativi).

Il secondo metodo aggiorna il profilo e conterrà la mia logica personalizzata per farlo. Ho incluso uno snippet su come analizzare i valori di querystring da un URL parziale. Ma l'unica cosa che deve davvero essere conosciuta qui è che questo è il metodo condiviso che aggiorna il profilo.

Importante: Affinché la chiamata AJAX funzioni, è necessario aggiungere il seguente gestore alla sezione system.web del file web.config:

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

Ho deciso che sarebbe stato meglio testare i cookie sul client e non effettuare la chiamata AJAX aggiuntiva se i cookie sono disabilitati. Per testare i cookie, utilizzare questo codice:

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() + ';' + ';';
}

Si tratta di 2 funzioni JavaScript (in un file .js personalizzato) che semplicemente scrivono un cookie e lo rileggono per determinare se i cookie possono essere letti. Quindi ripulisce il cookie impostando una data di scadenza nel passato.

<%@ 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

Questa è la nostra classe HttpHandler personalizzata che riceve la richiesta AJAX. La richiesta viene elaborata solo se il cookie .ASPXANONYMOUS viene passato (controllato ancora una volta utilizzando la classe AnonymousProfile dall'altro mio post), che impedirà a robot e altri script di eseguirlo.

Quindi eseguiamo del codice per aggiornare l'oggetto sessione, se necessario. Per qualche strana ragione, il valore IsNewSession rimarrà true fino a quando la sessione non viene effettivamente aggiornata, ma solo se un gestore per Session_Start non esiste in Global.asax. Quindi, per far funzionare questo codice sia con che senza un file Global.asax e senza alcun altro codice che aggiorni l'oggetto sessione, eseguiamo un aggiornamento qui.

Il prossimo pezzo di codice che ho preso da questo post e contiene una dipendenza dal serializzatore JSON.NET. Ero combattuto sull'utilizzo di questo approccio a causa della dipendenza aggiuntiva, ma alla fine ho deciso che il serializzatore JSON sarà probabilmente prezioso in futuro mentre continuo ad aggiungere AJAX e JQuery al sito.

Quindi otteniamo semplicemente i parametri e li passiamo al nostro metodo UpdateProfile condiviso nella classe PageBase definita in precedenza.

<!-- 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>

Infine, abbiamo la nostra sezione di configurazione per le proprietà del profilo, predisposta per essere utilizzata in modo anonimo (ho omesso di proposito la sezione della stringa di connessione, ma sono necessari anche una stringa di connessione e un database corrispondenti). La cosa principale da notare qui è l'inclusione dell'attributo eredita nel profilo. Questo è ancora una volta per la classe AnonymousProfile definita nel mio altro post .