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

时间:2015-10-12 19:12:26

标签: c# asp.net session iis webforms

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

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

<%@ Application Language="C#" %>

<script runat="server">
    void Application_AcquireRequestState(object sender, EventArgs e)
    {
        HttpContext.Current.Session["key"] = "value";
    }
</script>

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

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

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

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

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

  1. 的Default.aspx
  2. Default.asp的
  3. 的Default.htm
  4. ...
  5. 我仍然遇到同样的问题。

    更新

    事件处理程序只会被触发一次,因为它导致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. 会话为空
    2. 会话为空
    3. 会话不为空
    4. 会话为空
    5. 每次继续逐步执行代码时,只有当它开始执行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)
      10. 在#9,浏览器中的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的AJAX请求正在等待响应。

4 个答案:

答案 0 :(得分:11)

我发现a discussion关于通过显式网址提供网页与提供默认文档之间的差异。

使用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";
    }
}

答案 1 :(得分:1)

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

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

答案 2 :(得分:1)

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

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

所以解决方案很简单

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

<modules runAllManagedModulesForAllRequests="true">
     ....
</modules>

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

希望有帮助

答案 3 :(得分:0)

当资源有编译错误时,即使在Application_PostAcquireRequestState

中,会话也将为null