Jeg implementerede den løsning, jeg kom med i mit oprindelige indlæg, men det viste sig at være en smule anderledes end det, jeg oprindeligt beskrev. Rettelsen kan faktisk opdeles i 2 dele - en til at løse problemet med databasen, der opdateres, når cookies er deaktiveret, og den anden til at registrere, når cookies er deaktiveret uden at foretage en omdirigering.
Jeg har allerede sendt løsning på de anonyme profiler, der opretter registreringer, når cookies er deaktiveret .
Så nu vil jeg fokusere på den anden del - at få information ind i profilen fra den første side, der anmodes om. Dette skal kun gøres, hvis du laver analytics tracking eller noget lignende - den første del vil sørge for at beskytte databasen mod at blive fyldt op med totalt ubrugelige data, når 1) cookies er deaktiveret og 2) anonyme profilegenskaber bruges og fungerer fra den anden anmodning (eller første postback) og fremefter.
Da jeg undersøgte spørgsmålet om at kontrollere, om cookies er aktiveret, brugte de fleste løsninger en omdirigering enten til den samme side eller en anden side og tilbage igen. Interessant nok MSDN var den, der kom med 2-omdirigeringsløsningen.
Selvom en omdirigering under visse omstændigheder er acceptabel, ønskede jeg ikke, at den ekstra effekt på ydeevnen skulle påvirke størstedelen af vores brugere. I stedet valgte jeg en anden tilgang - brug AJAX til at køre kode på serveren efter den første anmodning er gennemført. Selvom dette har den fordel, at det ikke forårsager en omdirigering, har det den ulempe, at det ikke fungerer, når JavaScript er deaktiveret. Jeg valgte dog denne tilgang, fordi procentdelen af data, der går tabt ved den første anmodning, er ubetydelig, og selve applikationen afhænger ikke af disse data.
Så gå igennem fra begyndelsen af processen til slutningen...
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
Dette er Page_Load-metoden placeret i min brugerdefinerede PageBase-klasse, som alle siderne i projektet arver fra. Det første vi tjekker er, om dette er en ny session ved at tjekke egenskaben Session.IsNewSession. Denne egenskab er altid sand, hvis cookies er deaktiveret, eller hvis dette er den første anmodning. I begge tilfælde ønsker vi ikke at skrive til databasen.
Sektionen "else hvis" kører, hvis klienten accepterede sessionscookien, og dette ikke er den første anmodning til serveren. Det, der skal bemærkes ved dette kodestykke, er, at begge sektioner ikke kan køre i samme anmodning, hvilket betyder, at profilen kun kan opdateres 1 (eller 0) gange pr. anmodning.
Klassen AnonymousProfile er inkluderet i mine andet indlæg .
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
Dernæst har vi vores metode til at injicere et AJAX-kald på siden. Husk, at dette stadig er basisklassen, så uanset hvilken side brugeren lander på denne kode vil den køre på den første sideanmodning.
Inde i JavaScript tester vi først for at se, om cookies er aktiveret, og kalder i så fald en brugerdefineret handler på serveren ved hjælp af AJAX og JQuery. Vi overfører parametrene fra serveren til denne kode (selvom 2 af dem lige kunne være blevet leveret af klienten, er de ekstra bytes ikke så væsentlige).
Den anden metode opdaterer profilen og vil indeholde min brugerdefinerede logik til at gøre det. Jeg inkluderede et uddrag om, hvordan man analyserer querystring-værdierne fra en delvis URL. Men det eneste, der virkelig skal vides her, er, at dette er den delte metode, der opdaterer profilen.
Vigtigt: For at AJAX-kaldet skal fungere, skal følgende behandler tilføjes til system.web-sektionen i web.config-filen:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
Jeg besluttede, at det ville være bedst at teste for cookies på klienten og ikke foretage det ekstra AJAX-kald, hvis cookies er deaktiveret. For at teste cookies skal du bruge denne kode:
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() + ';' + ';';
}
Disse er 2 JavaScript-funktioner (i en brugerdefineret .js-fil), der blot skriver en cookie og læser den tilbage for at afgøre, om cookies kan læses. Det rydder derefter op i cookien ved at indstille en udløbsdato i fortiden.
<%@ 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
Dette er vores Custom HttpHandler-klasse, der modtager AJAX-anmodningen. Anmodningen behandles kun, hvis .ASPXANONYMOUS-cookien sendes ind (kontrolleres igen ved at bruge AnonymousProfile-klassen fra mit andet indlæg), hvilket vil forhindre robotter og andre scripts i at udføre den.
Dernæst kører vi noget kode for at opdatere sessionsobjektet, hvis det er nødvendigt. Af en eller anden mærkelig grund vil IsNewSession-værdien forblive sand, indtil sessionen faktisk er opdateret, men kun hvis en handler for Session_Start ikke findes i Global.asax. Så for at få denne kode til at fungere både med og uden en Global.asax-fil og uden anden kode, der opdaterer sessionsobjektet, kører vi en opdatering her.
Den næste kodebit greb jeg fra dette indlæg og indeholder en afhængighed til JSON.NET serializer. Jeg var revet med at bruge denne tilgang på grund af den ekstra afhængighed, men besluttede i sidste ende, at JSON serializer sandsynligvis vil være værdifuld i fremtiden, da jeg fortsætter med at tilføje AJAX og JQuery til webstedet.
Derefter henter vi simpelthen parametrene og sender dem til vores delte UpdateProfile-metode i PageBase-klassen, der blev defineret tidligere.
<!-- 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>
Til sidst har vi vores konfigurationssektion for profilegenskaberne, sat op til at blive brugt anonymt (jeg har med vilje udeladt forbindelsesstrengsektionen, men en tilsvarende forbindelsesstreng og database er også påkrævet). Det vigtigste at bemærke her er medtagelsen af arveegenskaben på profilen. Dette er endnu en gang for AnonymousProfile-klassen, der er defineret i min andet indlæg .