元の投稿で思いついた回避策を実装しましたが、最初に説明したものとは少し異なることが判明しました。この修正は、実際には 2 つの部分に分けることができます。1 つは、Cookie が無効になっているときにデータベースが更新される問題を修正するためのもので、もう 1 つは、リダイレクトを行わずに Cookie が無効になったことを検出するためのものです。
私はすでに Cookie が無効になっている場合に記録を作成する匿名プロファイルの解決策 .
ここでは、要求された最初のページからプロファイルに情報を取得するという 2 番目の部分に焦点を当てます。これは、分析追跡などを行っている場合にのみ実行する必要があります。最初の部分は、1) Cookie が無効で、2) 匿名プロファイル プロパティが使用されており、 2 回目のリクエスト (または最初のポストバック) 以降。
Cookie が有効になっているかどうかを確認する問題を調査したところ、ほとんどのソリューションでは、同じページまたは別のページにリダイレクトしてから再び戻るという方法が使用されていました。興味深いことに、MSDN 2 リダイレクト ソリューションを思いついたのは
でした。特定の状況ではリダイレクトは許容されますが、パフォーマンスへの余分な影響が大多数のユーザーに影響することは望んでいませんでした。代わりに、別のアプローチを選択しました。最初のリクエストが完了した後、AJAX を使用してサーバー上でコードを実行します。これにはリダイレクトが発生しないという利点がありますが、JavaScript が無効になっていると機能しないという欠点があります。ただし、最初の要求で失われるデータの割合は重要ではなく、アプリケーション自体はこのデータに依存しないため、このアプローチを選択しました。
それでは、プロセスの最初から最後までを順を追って説明します...
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
これは、プロジェクト内のすべてのページが継承するカスタム PageBase クラスに配置された Page_Load メソッドです。最初に、Session.IsNewSession プロパティをチェックして、これが新しいセッションかどうかを確認します。 Cookie が無効になっている場合、またはこれが最初の要求である場合、このプロパティは常に true です。どちらの場合も、データベースに書き込みたくありません。
「else if」セクションは、クライアントがセッション Cookie を受け入れ、これがサーバーへの最初の要求ではない場合に実行されます。このコード スニペットについて注意すべき点は、両方のセクションを同じリクエストで実行できないことです。つまり、プロファイルはリクエストごとに 1 回 (または 0 回) しか更新できません。
AnonymousProfile クラスは、私の 他の投稿 .
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
次に、ページに AJAX 呼び出しを挿入するメソッドがあります。これは基本クラスであるため、ユーザーがどのページにアクセスしても、このコードは最初のページ リクエストで実行されることに注意してください。
JavaScript の内部では、まず Cookie が有効になっているかどうかをテストし、有効になっている場合は、AJAX と JQuery を使用してサーバー上でカスタム ハンドラーを呼び出します。サーバーからこのコードにパラメーターを渡しています (そのうちの 2 つはクライアントによって提供された可能性がありますが、余分なバイトはそれほど重要ではありません)。
2 番目のメソッドはプロファイルを更新し、それを行うためのカスタム ロジックが含まれます。部分的な URL からクエリ文字列の値を解析する方法のスニペットを含めました。ただし、ここで知っておく必要がある唯一のことは、これがプロファイルを更新する共有メソッドであることです。
重要: AJAX 呼び出しを機能させるには、次のハンドラーを web.config ファイルの system.web セクションに追加する必要があります:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
クライアントで Cookie をテストし、Cookie が無効になっている場合は余分な AJAX 呼び出しを行わないことが最善であると判断しました。 Cookie をテストするには、次のコードを使用します:
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() + ';' + ';';
}
これらは 2 つの JavaScript 関数 (カスタム .js ファイル内) であり、単に Cookie を書き込み、それを読み戻して、Cookie を読み取ることができるかどうかを判断します。次に、過去の有効期限を設定して Cookie をクリーンアップします。
<%@ 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
これは、AJAX リクエストを受け取るカスタム HttpHandler クラスです。リクエストは、.ASPXANONYMOUS Cookie が渡された場合にのみ処理され (別の投稿の AnonymousProfile クラスを利用してもう一度チェックされます)、ロボットや他のスクリプトがそれを実行するのを防ぎます。
次に、必要に応じてセッション オブジェクトを更新するコードを実行します。奇妙な理由により、IsNewSession 値は、セッションが実際に更新されるまで true のままになりますが、それは、Session_Start のハンドラーが Global.asax に存在しない場合のみです。したがって、Global.asax ファイルの有無にかかわらず、またセッション オブジェクトを更新する他のコードがなくてもこのコードが機能するように、ここで更新を実行します。
この投稿 また、JSON.NET シリアライザーへの依存関係が含まれています。余分な依存関係があるため、このアプローチを使用するかどうか悩んでいましたが、サイトに AJAX と JQuery を追加し続けるにつれて、JSON シリアライザーが将来的に役立つ可能性が高いと最終的に判断しました.
次に、パラメータを取得して、前に定義した PageBase クラスの共有 UpdateProfile メソッドに渡します。
<!-- 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>
最後に、匿名で使用するように設定されたプロファイル プロパティの構成セクションがあります (意図的に接続文字列セクションを省略しましたが、対応する接続文字列とデータベースも必要です)。ここで注意すべき主なことは、プロファイルに inherits 属性が含まれていることです。これもまた、私の 他の投稿 .