在浏览器中加载虚拟目录名时,会话在AcquireRequestState中为空,但在加载Default.aspx时不为null

我有一个ASP.NET 4.0 WebForms应用程序。 我需要访问HttpContext.Current.Session并在Global.asax中的AcquireRequestState事件(或它后面的事件)中设置一个值,我发现了一种奇特的行为。

假设我在IIS(我的案例中为版本7)中有一个名为Foo的虚拟目录。 在那里我有Default.aspx作为主页。 Global.asax文件示例如下:

   void Application_AcquireRequestState(object sender, EventArgs e) { HttpContext.Current.Session["key"] = "value"; }  

当我在浏览器中访问http://localhost/Foo/Default.aspx时,它运行正常。 当我访问http://localhost/Foo/我得到一个NullReferenceException ,我在会话中设置值。 唯一的变化是浏览器中的URL。 它们最终会访问同一页面,但框架的行为会有所不同,具体取决于URL是否只包含文件夹名称,还是包含aspx文件。

检查if (HttpContext.Current.Session != null)对我来说不是一个选项,因为我需要在会话中为每个请求设置一个值,这是不可协商的。

IIS中是否存在我缺少的配置设置,或者这是一个错误/被遗忘的function?

另一个问题的答案暗示IIS不会为每种请求加载会话,例如样式表不需要会话。 也许这种情况正在发生,因为IIS无法提前判断该文件夹名称是否会导致执行aspx文件或者是否会提供静态HTML文件?

更新:我甚至尝试重新排序IIS查找的默认文档,以便“default.aspx”位于列表的顶部,例如

  1. Default.aspx的
  2. Default.asp的
  3. Default.htm的

我仍然遇到同样的问题。

更新:

事件处理程序只被触发一次,因为它导致NullReferenceException 。 我已经做了一些额外的阅读,我知道ASP.NET会为每个请求触发这些事件,即使对于CSS或JavaScript文件也是如此。 此外,没有为静态文件加载会话对象,因为没有访问会话的代码,因此无需加载对象。 即便如此,第一个请求是对网页的请求,该请求将需要会话,并且会话为空。

@DmytroShevchenko问:

首先添加一个保护检查if (HttpContext.Current.Session != null) ,这样就不会抛出NullReferenceException 。 然后尝试看一下,也许这个事件会被第二次发射,并且会话可用。

修改后的代码

 void Application_AcquireRequestState(object sender, EventArgs e) { if (HttpContext.Current.Session != null) { HttpContext.Current.Session["key"] = "value"; } } 

我在if语句中设置了一个断点。 我看到这个事件发生了4次:

  1. session为null
  2. session为null
  3. 会话不为空
  4. session为null

每次继续遍历代码时,只有当它开始执行Default.aspx及其代码隐藏时,我才有可用的会话。 我实际上在Firefox中打开了网页,并监控网络请求。 第一个请求是http://localhost/Foo/

接下来我在Application_BeginRequest设置了一个断点,并得到以下事件:

  1. 的BeginRequest
  2. 的AcquireRequestState
  3. 的BeginRequest
  4. 的AcquireRequestState
  5. 的BeginRequest
  6. AcquireRequestState(会话不为空)
  7. 执行Default.aspx(/ Foo返回对浏览器的响应)
  8. 的BeginRequest
  9. AcquireRequestState(会话再次为null)

在#9浏览器中的AJAX请求http://localhost:54859/8fad4e71e57a4caebe1c6ed7af6f583a/arterySignalR/poll?transport=longPolling&connectionToken=...&messageId=...&requestUrl=http%3A%2F%2Flocalhost%2FFoo%2F&browserName=Firefox&userAgent=Mozilla%2F5.0+(Windows+NT+6.1%3B+WOW64%3B+rv%3A41.0)+Gecko%2F20100101+Firefox%2F41.0&tid=4&_=1445346977956正在等待响应。

我找到关于通过显式URL提供页面与提供默认文档之间的区别的讨论

使用MVC和WebAPI,引入了一个新的HttpModule: ExtensionlessUrlHandler 。 我相信你的事件多次激活(并且只有一次会话可用)可能是由这个模块或ASP.NET的其他(重新)路由逻辑引起的,它实际上重定向ASP.NET以处理Default.aspx

此外,正如您自己提到的,可以为静态文件请求触发这些事件。

最重要的是, 每次发生事件 ,您都不应该依赖会话。 但是可以安全地假设您在提供ASP.NET页面时至少可以访问一次会话。 因此,这就是你的代码应该是这样的:

 void Application_AcquireRequestState(object sender, EventArgs e) { if (HttpContext.Current.Session != null) { HttpContext.Current.Session["key"] = "value"; } } 

发生的事情是,在将请求传输到页面之前发生的某些事件会触发此事件。 同样,此事件可以由ASPX以外的页面引发。 我想你需要做一些事情:

  • 在页面代码中将AutoEventWireUp设置为false (在default.aspx中)。 这看似奇怪而且无关,但显然这可以解决你的问题 。
  • 检查是否为null 。 真。 因为在所有事件中,您只对源自实际具有会话状态的ASPX页面(或类似)的事件感兴趣。 由于用户的每个真实请求始终会创建会话,因此您只需过滤这些会话,而无需担心错过某些事件。 如您所见,在每个周期中,始终至少有一个具有设置会话的事件命中。
  • 使用PostAcquireRequestState ,它是更自然的使用(或使用PreRequestHandlerExecute ),因为在该事件上,所有状态都保证被设置和填充。

我认为您的请求url不包含“.aspx”,对吧?

IIS7 +版本的配置whitch在认为请求处理程序不是managedHandler时不会使用“SessionStateModule”

所以解决方案很简单

找到您的web.config,然后添加该属性

  ....  

runAllManagedModulesForAllRequests =“true”告诉asp.net无论如何都要使用所有模块

希望有帮助

当资源有编译错误时,即使在Application_PostAcquireRequestState中,会话也将为null