Eu implementei a solução alternativa que criei no meu post original, no entanto, acabou sendo um pouco diferente do que descrevi originalmente. A correção pode ser dividida em 2 partes - uma para corrigir o problema com o banco de dados sendo atualizado quando os cookies são desabilitados e a segunda para detectar quando os cookies são desabilitados sem fazer um redirecionamento.
Já postei o solução para os perfis anônimos que criam registros quando os cookies são desabilitados .
Então agora vou me concentrar na segunda parte - obter informações no perfil da primeira página solicitada. Isso só precisa ser feito se você estiver fazendo rastreamento analítico ou algo semelhante - a primeira parte cuidará de proteger o banco de dados de se encher de dados totalmente inúteis quando 1) os cookies estiverem desativados e 2) as propriedades do perfil anônimo forem usadas e funcionarem a partir a segunda solicitação (ou primeiro postback) em diante.
Quando pesquisei o problema de verificar se os cookies estão ativados, a maioria das soluções usava um redirecionamento para a mesma página ou para uma página diferente e vice-versa. Curiosamente, MSDN foi quem apresentou a solução de redirecionamento 2.
Embora em certas circunstâncias um redirecionamento seja aceitável, eu não queria que o impacto extra no desempenho afetasse a maioria dos nossos usuários. Em vez disso, optei por outra abordagem - use AJAX para executar o código no servidor após a conclusão da primeira solicitação. Embora isso tenha a vantagem de não causar um redirecionamento, tem a desvantagem de não funcionar quando o JavaScript está desabilitado. No entanto, optei por essa abordagem porque a porcentagem de dados que estão sendo perdidos na solicitação inicial é insignificante e o próprio aplicativo não depende desses dados.
Então, percorrendo desde o início do processo até o fim...
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 é o método Page_Load colocado na minha classe PageBase personalizada, da qual todas as páginas do projeto herdam. A primeira coisa que verificamos é se esta é uma nova sessão verificando a propriedade Session.IsNewSession. Esta propriedade é sempre verdadeira se os cookies estiverem desabilitados ou se esta for a primeira solicitação. Em ambos os casos, não queremos escrever no banco de dados.
A seção "else if" é executada se o cliente aceitou o cookie de sessão e esta não é a primeira solicitação ao servidor. A coisa a notar sobre este trecho de código é que ambas as seções não podem ser executadas na mesma solicitação, o que significa que o perfil só pode ser atualizado 1 (ou 0) vezes por solicitação.
A classe AnonymousProfile está incluída em meus outra postagem .
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
Em seguida, temos nosso método para injetar uma chamada AJAX na página. Lembre-se de que essa ainda é a classe base, portanto, independentemente da página que o usuário acessar, esse código será executado na solicitação da primeira página.
Dentro do JavaScript, primeiro testamos para ver se os cookies estão habilitados e, em caso afirmativo, chamamos um manipulador personalizado no servidor usando AJAX e JQuery. Estamos passando os parâmetros do servidor para este código (embora 2 deles possam ter sido fornecidos pelo cliente, os bytes extras não são tão significativos).
O segundo método atualiza o perfil e conterá minha lógica personalizada para fazer isso. Incluí um trecho sobre como analisar os valores de querystring de um URL parcial. Mas a única coisa que realmente precisa ser conhecida aqui é que esse é o método compartilhado que atualiza o perfil.
Importante: Para que a chamada AJAX funcione, o seguinte manipulador deve ser adicionado à seção system.web do arquivo web.config:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
Decidi que seria melhor testar cookies no cliente e não fazer a chamada AJAX extra se os cookies estiverem desabilitados. Para testar os cookies, use 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() + ';' + ';';
}
Essas são 2 funções JavaScript (em um arquivo .js personalizado) que simplesmente gravam um cookie e o lêem de volta para determinar se os cookies podem ser lidos. Em seguida, ele limpa o cookie definindo uma data de expiração no passado.
<%@ 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 é a nossa classe Custom HttpHandler que recebe a solicitação AJAX. A solicitação é processada apenas se o cookie .ASPXANONYMOUS for passado (verificado mais uma vez utilizando a classe AnonymousProfile de meu outro post), o que impedirá que robôs e outros scripts o executem.
Em seguida, executamos algum código para atualizar o objeto de sessão, se necessário. Por algum motivo estranho, o valor IsNewSession permanecerá verdadeiro até que a sessão seja realmente atualizada, mas somente se um manipulador para Session_Start não existir no Global.asax. Portanto, para que esse código funcione com e sem um arquivo Global.asax e sem qualquer outro código que atualize o objeto de sessão, executamos uma atualização aqui.
O próximo pedaço de código que peguei de esta postagem e contém uma dependência do serializador JSON.NET. Eu estava preocupado em usar essa abordagem por causa da dependência extra, mas finalmente decidi que o serializador JSON provavelmente será valioso no futuro à medida que continuar adicionando AJAX e JQuery ao site.
Em seguida, simplesmente obtemos os parâmetros e os passamos para nosso método UpdateProfile compartilhado na classe PageBase que foi definida anteriormente.
<!-- 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, temos nossa seção de configuração para as propriedades do perfil, configuradas para serem usadas anonimamente (eu omiti propositalmente a seção de string de conexão, mas uma string de conexão e banco de dados correspondentes também são necessários). A principal coisa a notar aqui é a inclusão do atributo inherits no perfil. Mais uma vez, isso é para a classe AnonymousProfile definida em meu outra postagem .