我可以为此使用什么类型的变量?

What type of variable could I use for this?

我想做的事情原则上很简单,但是 ASP.NET 页面的生命周期给我的一天泼了一盆冷水。

问题来了

我们已经实现了 URL 重定向,我在 Global.ASAX 中继承了这样的代码,在 Sub Application_BeginRequest:

    dv.Table = CommonFunctions.ConvertXmlFileToDataSet("/XmlData/WebAppFolders.xml").Tables("Application")
    dv.RowFilter = "'" + fullOrigionalPath + "' LIKE '%'+ folder + '%'"
    If dv.Count > 0 Then 'match on key to redirect
        If fullOrigionalPath.EndsWith(dv(0)("folder") + "/") Then 
            Context.RewritePath(dv(0)("basePage"), True)
        Else 'missing page in directory --> redirect
            HttpContext.Current.Items("Raise404") = "true"
            Response.Redirect("/" + dv(0)("folder"))
        End If
        Return
    End If

基本上我们正在读取一个包含 URL 重定向的大型 XML 文件。那工作正常。问题发生在行...

HttpContext.Current.Items("Raise404") = "true"

在 Application_BeginRequest 的上下文中,会话对象尚不可用,因此我无法使用它来存储我命名为 Raise404 的标志。相反,我不得不求助于使用 Items 集合。

发生重定向时出现问题。新的页面生命周期破坏了 Items 数组并用一个新的空数组覆盖它。

当我尝试使用我的标记 Raise404 时,它已不存在于我的页面 PreRender 事件中。

为了使事情复杂化,我们使用母版页,所以我被要求将我们要执行的代码放在母版页中

理想情况下,如果 Items 数组没有被销毁,则此代码可以工作:

Private Sub Page_PreRender(sender As Object, e As System.EventArgs) Handles Me.PreRender
    If HttpContext.Current.Items("Raise404") IsNot Nothing AndAlso HttpContext.Current.Items("Raise404").Equals("true") Then
        Response.StatusCode = 404
        HttpContext.Current.Items("Raise404") = Nothing
    End If
End Sub

我不确定我可以使用哪种变量来存储我的标志并允许它在重定向后继续存在。

有什么想法吗?

更新:问题是为我的请求提供服务的 HTTP 处理程序是 System.Web.DefaultHTTPHandler,它没有实现 IRequiresSessionState,因此当我的请求在 Global ASAX 中处理时,没有创建会话。因此,解决方案似乎是编写一个实现 IRequiresSessionState 的自定义 HTTPHandler,并将其用于我所有的 .aspx 文件。即使这样,在引发 PreRequestHandlerExecute 之前,也不会在 Global.ASAX 中创建会话状态。因此,将它们放在一起,我认为我需要编写一个实现 IRequiresSessionState 的自定义 HTTP 处理程序,并延迟页面的重定向,直到引发 PreRequestHandlerExecute,我才能将我的标志存储到会话状态中,并且只有在那之后,重定向我的页面。

不是很优雅,不知道会不会影响性能。

您是否考虑过为此使用查询字符串?

Response.Redirect("/" & dv(0)("folder") & "?Raise404=true")

然后,在您的母版页中,只需选中 QueryString("Raise404") 并采取相应的行动。

我能看到的唯一缺点是恶意客户端可以故意 添加 Raise404=true 到查询字符串,这对于您当前的解决方案是不可能的。但是,我看不出这有什么坏处。

正如我所怀疑的,我的问题与以下事实直接相关:如果出于性能原因 IIS 8 检测到不需要会话对象,那么 IIS 8 将避免使用实现 IRequiresSessionState 的 HTTPHandler,因此重定向代码正在发生在 Application_BeginRequest 事件期间,IIS 一直在使用 System.Web.DefaultHTTPHandler.

处理我的请求

所以,经过进一步的研究,我发现这个帮助很大: IRequiresSessionState - how do I use it?

并且我也在这篇文章中记录了自己: http://forums.iis.net/t/1094546.aspx

解决我问题的方法是编写一个虚拟的 HTTPHandler class。我在我的 app_code 文件夹中添加了一个新的 class:

Imports System.Web

    Public Class HTTPRequestHandler : Implements IHttpHandler, IRequiresSessionState

        Private OriginalHandler As IHttpHandler

        Public Sub New(handler As IHttpHandler)
            Me.OriginalHandler = handler
        End Sub

        Public Sub ProcessRequest(context As HttpContext) Implements IHttpHandler.ProcessRequest
            Throw New InvalidOperationException("HTTPRequestHandler cannot process requests.")
        End Sub

        Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
            Get
                Return True
            End Get
        End Property

        Public ReadOnly Property Handler() As IHttpHandler
            Get
                If Me.OriginalHandler IsNot Nothing Then
                    Return Me.OriginalHandler
                End If
                Return HttpContext.Current.Handler
            End Get
        End Property
    End Class

现在,在 Application_BeginRequest 期间,会话不可用,但 HttpContext.Current.Items 可用。因此,正如第二篇文章所建议的那样,在 PostMapRequestHandler 期间我提出了一个条件:

If HttpContext.Current.Items("Raise404") IsNot Nothing Then
    Context.Handler = New MyNameSpace.HTTPRequestHandler(Context.Handler)
End If

最后,创建了我最终可以在 PreRequestHandlerExecute 期间使用的会话对象:

    HttpContext.Current.Session("Raise404") = "true"
    Response.Redirect(HttpContext.Current.Items("Redirect"))

我绝对不会争辩说这不优雅,但它确实有效,我可以在 Page_PreRender 期间在我的母版页上提出我的 404 错误。

希望这对其他人有帮助。

干杯。