sql >> Base de Datos >  >> RDS >> Sqlserver

¿Existen alternativas más eficientes a los perfiles anónimos de ASP.NET 2.0?

Implementé la solución alternativa que se me ocurrió en mi publicación original, sin embargo, resultó ser un poco diferente de lo que describí originalmente. En realidad, la solución se puede dividir en 2 partes:una para solucionar el problema con la base de datos que se actualiza cuando las cookies están deshabilitadas y la segunda para detectar cuándo las cookies están deshabilitadas sin hacer una redirección.

Ya publiqué el solución a la creación de registros de perfiles anónimos cuando las cookies están deshabilitadas .

Así que ahora me centraré en la segunda parte:obtener información en el perfil desde la primera página solicitada. Esto solo debe hacerse si está realizando un seguimiento analítico o algo similar:la primera parte se encargará de proteger la base de datos para que no se llene con datos totalmente inútiles cuando 1) las cookies están deshabilitadas y 2) se usan propiedades de perfil anónimo y funciona desde la segunda solicitud (o la primera devolución de datos) en adelante.

Cuando investigué el problema de verificar si las cookies están habilitadas, la mayoría de las soluciones utilizaron una redirección a la misma página o a una página diferente y viceversa. Curiosamente, MSDN fue quien ideó la solución de 2 redireccionamientos.

Si bien en ciertas circunstancias una redirección es aceptable, no quería que el impacto adicional en el rendimiento afectara a la mayoría de nuestros usuarios. En cambio, opté por otro enfoque:use AJAX para ejecutar el código en el servidor después de que se haya completado la primera solicitud. Si bien esto tiene la ventaja de no causar una redirección, tiene la desventaja de no funcionar cuando JavaScript está deshabilitado. Sin embargo, opté por este enfoque porque el porcentaje de datos que se pierden en la solicitud inicial es insignificante y la aplicación en sí no depende de estos datos.

Entonces, recorriendo desde el principio del proceso hasta el final...

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

Este es el método Page_Load colocado en mi clase PageBase personalizada, de la cual heredan todas las páginas del proyecto. Lo primero que comprobamos es si se trata de una nueva sesión comprobando la propiedad Session.IsNewSession. Esta propiedad siempre es verdadera si las cookies están deshabilitadas o si esta es la primera solicitud. En ambos casos, no queremos escribir en la base de datos.

La sección "si no" se ejecuta si el cliente aceptó la cookie de sesión y esta no es la primera solicitud al servidor. Lo que hay que tener en cuenta sobre este fragmento de código es que ambas secciones no pueden ejecutarse en la misma solicitud, lo que significa que el perfil solo se puede actualizar 1 (o 0) veces por solicitud.

La clase AnonymousProfile está incluida en mi otra publicación .

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

A continuación, tenemos nuestro método para inyectar una llamada AJAX en la página. Tenga en cuenta que esta sigue siendo la clase base, por lo que, independientemente de la página a la que llegue el usuario, este código se ejecutará en la solicitud de la primera página.

Dentro del JavaScript, primero probamos para ver si las cookies están habilitadas y, si es así, llamamos a un controlador personalizado en el servidor usando AJAX y JQuery. Estamos pasando los parámetros del servidor a este código (aunque 2 de ellos podrían haber sido proporcionados por el cliente, los bytes adicionales no son tan significativos).

El segundo método actualiza el perfil y contendrá mi lógica personalizada para hacerlo. Incluí un fragmento sobre cómo analizar los valores de cadena de consulta de una URL parcial. Pero lo único que realmente debe saberse aquí es que este es el método compartido que actualiza el perfil.

Importante: Para que funcione la llamada AJAX, se debe agregar el siguiente controlador a la sección system.web del archivo web.config:

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

Decidí que sería mejor probar las cookies en el cliente y no hacer la llamada AJAX adicional si las cookies están deshabilitadas. Para probar las cookies, utilice este código:

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

Estas son 2 funciones de JavaScript (en un archivo .js personalizado) que simplemente escriben una cookie y la vuelven a leer para determinar si se pueden leer las cookies. Luego limpia la cookie estableciendo una fecha de caducidad en el pasado.

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

Esta es nuestra clase HttpHandler personalizada que recibe la solicitud AJAX. La solicitud se procesa solo si se pasa la cookie .ASPXANONYMOUS (verificada una vez más utilizando la clase AnonymousProfile de mi otra publicación), lo que evitará que los robots y otros scripts la ejecuten.

A continuación, ejecutamos un código para actualizar el objeto de la sesión si es necesario. Por alguna extraña razón, el valor de IsNewSession permanecerá verdadero hasta que la sesión se actualice realmente, pero solo si no existe un controlador para Session_Start en Global.asax. Entonces, para que este código funcione con y sin un archivo Global.asax y sin ningún otro código que actualice el objeto de la sesión, ejecutamos una actualización aquí.

El siguiente fragmento de código lo tomé de esta publicación y contiene una dependencia al serializador JSON.NET. Estaba indeciso sobre el uso de este enfoque debido a la dependencia adicional, pero finalmente decidí que el serializador JSON probablemente será valioso en el futuro a medida que continúe agregando AJAX y JQuery al sitio.

Luego simplemente obtenemos los parámetros y los pasamos a nuestro método compartido UpdateProfile en la clase PageBase que se definió previamente.

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

Por último, tenemos nuestra sección de configuración para las propiedades del perfil, configurada para usarse de forma anónima (omití deliberadamente la sección de la cadena de conexión, pero también se requieren una cadena de conexión y una base de datos correspondientes). Lo principal a tener en cuenta aquí es la inclusión del atributo de herencia en el perfil. Una vez más, esto es para la clase AnonymousProfile que se define en mi otras publicaciones .