J'ai implémenté la solution de contournement que j'ai proposée dans mon message d'origine, mais elle s'est avérée un peu différente de ce que j'avais décrit à l'origine. Le correctif peut en fait être divisé en 2 parties - une pour résoudre le problème de mise à jour de la base de données lorsque les cookies sont désactivés, et la seconde pour détecter lorsque les cookies sont désactivés sans effectuer de redirection.
J'ai déjà posté le solution aux profils anonymes créant des enregistrements lorsque les cookies sont désactivés .
Alors maintenant, je vais me concentrer sur la deuxième partie - obtenir des informations dans le profil à partir de la première page demandée. Cela ne doit être fait que si vous effectuez un suivi analytique ou quelque chose de similaire - la première partie s'occupera de protéger la base de données contre le remplissage de données totalement inutiles lorsque 1) les cookies sont désactivés et 2) les propriétés de profil anonymes sont utilisées et fonctionnent à partir de à partir de la deuxième requête (ou de la première publication).
Lorsque j'ai étudié la question de vérifier si les cookies sont activés, la plupart des solutions utilisaient une redirection vers la même page ou une page différente et vice-versa. Fait intéressant, MSDN est celui qui a proposé la solution à 2 redirections.
Bien que dans certaines circonstances, une redirection soit acceptable, je ne voulais pas que l'impact supplémentaire sur les performances affecte la majorité de nos utilisateurs. Au lieu de cela, j'ai opté pour une autre approche - utiliser AJAX pour exécuter le code sur le serveur une fois la première requête terminée. Bien que cela ait l'avantage de ne pas provoquer de redirection, il a l'inconvénient de ne pas fonctionner lorsque JavaScript est désactivé. Cependant, j'ai opté pour cette approche car le pourcentage de données perdues lors de la demande initiale est insignifiant et l'application elle-même ne dépend pas de ces données.
Donc, en parcourant le processus du début à la fin...
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
Il s'agit de la méthode Page_Load placée dans ma classe PageBase personnalisée, dont héritent toutes les pages du projet. La première chose que nous vérifions est s'il s'agit d'une nouvelle session en vérifiant la propriété Session.IsNewSession. Cette propriété est toujours vraie si les cookies sont désactivés ou s'il s'agit de la première demande. Dans les deux cas, nous ne voulons pas écrire dans la base de données.
La section "else if" s'exécute si le client a accepté le cookie de session et qu'il ne s'agit pas de la première requête adressée au serveur. La chose à noter à propos de cet extrait de code est que les deux sections ne peuvent pas s'exécuter dans la même requête, ce qui signifie que le profil ne peut être mis à jour qu'une (ou 0) fois par requête.
La classe AnonymousProfile est incluse dans mon autre message .
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
Ensuite, nous avons notre méthode pour injecter un appel AJAX dans la page. Gardez à l'esprit qu'il s'agit toujours de la classe de base, donc quelle que soit la page sur laquelle l'utilisateur atterrit, ce code s'exécutera à la première demande de page.
Dans le JavaScript, nous testons d'abord si les cookies sont activés et, si c'est le cas, nous appelons un gestionnaire personnalisé sur le serveur à l'aide d'AJAX et de JQuery. Nous transmettons les paramètres du serveur dans ce code (bien que 2 d'entre eux aient pu être fournis par le client, les octets supplémentaires ne sont pas si significatifs).
La deuxième méthode met à jour le profil et contiendra ma logique personnalisée pour le faire. J'ai inclus un extrait sur la façon d'analyser les valeurs de la chaîne de requête à partir d'une URL partielle. Mais la seule chose qu'il faut vraiment savoir ici, c'est qu'il s'agit de la méthode partagée qui met à jour le profil.
Important : Pour que l'appel AJAX fonctionne, le gestionnaire suivant doit être ajouté à la section system.web du fichier web.config :
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
J'ai décidé qu'il serait préférable de tester les cookies sur le client et de ne pas effectuer l'appel AJAX supplémentaire si les cookies sont désactivés. Pour tester les cookies, utilisez ce 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() + ';' + ';';
}
Il s'agit de 2 fonctions JavaScript (dans un fichier .js personnalisé) qui écrivent simplement un cookie et le relisent pour déterminer si les cookies peuvent être lus. Il nettoie ensuite le cookie en définissant une date d'expiration dans le passé.
<%@ 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
Il s'agit de notre classe Custom HttpHandler qui reçoit la requête AJAX. La demande n'est traitée que si le cookie .ASPXANONYMOUS est transmis (vérifié une fois de plus en utilisant la classe AnonymousProfile de mon autre message), ce qui empêchera les robots et autres scripts de l'exécuter.
Ensuite, nous exécutons du code pour mettre à jour l'objet de session si nécessaire. Pour une raison étrange, la valeur IsNewSession restera vraie jusqu'à ce que la session soit réellement mise à jour, mais uniquement si un gestionnaire pour Session_Start n'existe pas dans Global.asax. Donc, pour que ce code fonctionne à la fois avec et sans fichier Global.asax et sans aucun autre code qui met à jour l'objet de session, nous exécutons une mise à jour ici.
Le prochain morceau de code que j'ai récupéré de ce message et contient une dépendance au sérialiseur JSON.NET. J'étais déchiré à l'idée d'utiliser cette approche en raison de la dépendance supplémentaire, mais j'ai finalement décidé que le sérialiseur JSON serait probablement utile à l'avenir alors que je continue à ajouter AJAX et JQuery au site.
Ensuite, nous récupérons simplement les paramètres et les transmettons à notre méthode partagée UpdateProfile dans la classe PageBase qui a été définie précédemment.
<!-- 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>
Enfin, nous avons notre section de configuration pour les propriétés de profil, configurée pour être utilisée de manière anonyme (j'ai volontairement omis la section de chaîne de connexion, mais une chaîne de connexion et une base de données correspondantes sont également requises). La principale chose à noter ici est l'inclusion de l'attribut inherits sur le profil. Ceci est encore une fois pour la classe AnonymousProfile qui est définie dans mon autre message .