Tôi đã triển khai cách giải quyết mà tôi đã đưa ra trong bài đăng ban đầu của mình, tuy nhiên, nó hóa ra hơi khác so với những gì tôi đã mô tả ban đầu. Bản sửa lỗi thực sự có thể được chia thành 2 phần - một phần để khắc phục sự cố với cơ sở dữ liệu được cập nhật khi cookie bị tắt và phần thứ hai để phát hiện khi cookie bị vô hiệu hóa mà không thực hiện chuyển hướng.
Tôi đã đăng giải pháp cho các cấu hình ẩn danh tạo hồ sơ khi cookie bị tắt .
Vì vậy, bây giờ tôi sẽ tập trung vào phần thứ hai - lấy thông tin vào hồ sơ từ trang đầu tiên được yêu cầu. Điều này chỉ cần được thực hiện nếu bạn đang theo dõi phân tích hoặc một cái gì đó tương tự - phần đầu tiên sẽ chăm sóc bảo vệ cơ sở dữ liệu không bị lấp đầy bởi dữ liệu hoàn toàn vô dụng khi 1) cookie bị vô hiệu hóa và 2) thuộc tính hồ sơ ẩn danh được sử dụng và hoạt động từ yêu cầu thứ hai (hoặc đăng lại đầu tiên) trở đi.
Khi tôi nghiên cứu vấn đề kiểm tra xem cookie có được bật hay không, hầu hết các giải pháp đều sử dụng chuyển hướng đến cùng một trang hoặc một trang khác và quay lại một lần nữa. Thật thú vị, MSDN là người đã đưa ra giải pháp chuyển hướng 2.
Mặc dù trong một số trường hợp nhất định, chuyển hướng có thể chấp nhận được, nhưng tôi không muốn tác động thêm về hiệu suất ảnh hưởng đến phần lớn người dùng của chúng tôi. Thay vào đó, tôi đã chọn một cách tiếp cận khác - sử dụng AJAX để chạy mã trên máy chủ sau khi yêu cầu đầu tiên hoàn thành. Mặc dù điều này có ưu điểm là không gây ra chuyển hướng, nhưng nó có nhược điểm là không hoạt động khi JavaScript bị vô hiệu hóa. Tuy nhiên, tôi đã chọn cách tiếp cận này vì phần trăm dữ liệu bị mất theo yêu cầu ban đầu là không đáng kể và bản thân ứng dụng không phụ thuộc vào dữ liệu này.
Vì vậy, đi qua từ đầu của quá trình cho đến khi kết thúc ...
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
Đây là phương thức Page_Load được đặt trong lớp PageBase tùy chỉnh của tôi, mà tất cả các trang trong dự án đều kế thừa. Điều đầu tiên chúng ta kiểm tra xem đây có phải là một phiên mới hay không bằng cách kiểm tra thuộc tính Session.IsNewSession. Thuộc tính này luôn đúng nếu cookie bị vô hiệu hóa hoặc nếu đây là yêu cầu đầu tiên. Trong cả hai trường hợp, chúng tôi không muốn ghi vào cơ sở dữ liệu.
Phần "khác nếu" chạy nếu máy khách chấp nhận cookie phiên và đây không phải là yêu cầu đầu tiên đến máy chủ. Điều cần lưu ý về đoạn mã này là cả hai phần không thể chạy trong cùng một yêu cầu, có nghĩa là cấu hình chỉ có thể được cập nhật 1 (hoặc 0) lần cho mỗi yêu cầu.
Lớp AnonymousProfile được bao gồm trong bài đăng khác .
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
Tiếp theo, chúng ta có phương pháp để đưa một lệnh gọi AJAX vào trang. Xin lưu ý rằng đây vẫn là lớp cơ sở nên bất kể trang nào mà người dùng truy cập vào mã này sẽ chạy trên yêu cầu trang đầu tiên.
Bên trong JavaScript, trước tiên, chúng tôi kiểm tra xem cookie có được bật hay không và nếu có, hãy gọi trình xử lý tùy chỉnh trên máy chủ bằng AJAX và JQuery. Chúng tôi đang chuyển các tham số từ máy chủ vào mã này (mặc dù 2 trong số chúng có thể do khách hàng cung cấp, các byte thừa không đáng kể).
Phương pháp thứ hai cập nhật hồ sơ và sẽ chứa logic tùy chỉnh của tôi để làm như vậy. Tôi đã bao gồm một đoạn mã về cách phân tích cú pháp các giá trị chuỗi truy vấn từ một phần URL. Nhưng điều duy nhất thực sự cần biết ở đây là đây là phương pháp được chia sẻ để cập nhật hồ sơ.
Quan trọng: Đối với lệnh gọi AJAX đến chức năng, trình xử lý sau phải được thêm vào phần system.web của tệp web.config:
<httpModules>
<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
</httpModules>
Tôi quyết định tốt nhất là nên kiểm tra cookie trên máy khách và không thực hiện lệnh gọi AJAX bổ sung nếu cookie bị tắt. Để kiểm tra cookie, hãy sử dụng mã này:
Hàmfunction 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() + ';' + ';';
}
Đây là 2 hàm JavaScript (trong tệp .js tùy chỉnh) chỉ cần viết một cookie và đọc lại để xác định xem cookie có thể đọc được hay không. Sau đó, nó sẽ xóa cookie bằng cách đặt ngày hết hạn trong quá khứ.
<%@ 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
Đây là lớp HttpHandler tùy chỉnh của chúng tôi nhận yêu cầu AJAX. Yêu cầu chỉ được xử lý nếu cookie .ASPXANONYMOUS được chuyển vào (được kiểm tra một lần nữa bằng cách sử dụng lớp AnonymousProfile từ bài đăng khác của tôi), điều này sẽ ngăn không cho rô bốt và các tập lệnh khác thực thi nó.
Tiếp theo, chúng tôi chạy một số mã để cập nhật đối tượng phiên nếu nó được yêu cầu. Vì một số lý do kỳ lạ, giá trị IsNewSession sẽ vẫn đúng cho đến khi phiên thực sự được cập nhật, nhưng chỉ khi một trình xử lý cho Session_Start không tồn tại trong Global.asax. Vì vậy, để làm cho mã này hoạt động cả khi có và không có tệp Global.asax và không có bất kỳ mã nào khác cập nhật đối tượng phiên, chúng tôi chạy bản cập nhật tại đây.
Đoạn mã tiếp theo tôi lấy từ bài đăng này và chứa phần phụ thuộc vào bộ tuần tự JSON.NET. Tôi đã rất khó khăn khi sử dụng phương pháp này vì phụ thuộc nhiều hơn, nhưng cuối cùng quyết định rằng trình tuần tự JSON có thể sẽ có giá trị trong tương lai khi tôi tiếp tục thêm AJAX và JQuery vào trang web.
Sau đó, chúng tôi chỉ cần lấy các tham số và chuyển chúng vào phương thức UpdateProfile được chia sẻ của chúng tôi trong lớp PageBase đã được xác định trước đó.
<!-- 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>
Cuối cùng, chúng tôi có phần cấu hình của chúng tôi cho các thuộc tính hồ sơ, được thiết lập để sử dụng ẩn danh (tôi cố tình bỏ qua phần chuỗi kết nối, nhưng một chuỗi kết nối và cơ sở dữ liệu tương ứng cũng được yêu cầu). Điều chính cần lưu ý ở đây là việc đưa thuộc tính kế thừa vào hồ sơ. Điều này một lần nữa dành cho lớp AnonymousProfile được xác định trong bài đăng khác .