在Asp.net路由中,IRouteHandler.GetHttpHandler中的会话为空。

5

我正在尝试在IRouteHandler类的GettHttpHandler方法中启用Session,但是session始终为null。请问我做错了什么?

在global.asax中,我有以下代码:

RouteTable.Routes.Add("All", new Route("{*page}", new MyRouteHandler()));

MyRouteHandler类中Session为null的情况如下:
public class MyRouteHandler : IRouteHandler, IRequiresSessionState
{
    public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        string test = HttpContext.Current.Session["test"].ToString();
        return BuildManager.CreateInstanceFromVirtualPath("~/Page.aspx", typeof(Page)) as Page;
    }
}

我制作了一个小的测试应用程序,展示了这个问题。请看测试应用程序
有没有人能告诉我哪里做错了?
编辑后添加:
是的,我确实需要在路由处理程序中使用会话数据。原因有很多,但其中一个容易解释的原因是当用户可以切换到预览模式浏览网站时。
该网站由数据库中的动态页面层次结构(/page1/page2...)组成,可以正常发布或预览。浏览网站的内容生产者可以选择只查看普通页面还是也查看已发布的预览页面。浏览模式存储在用户的会话中,因此路由处理程序需要知道浏览模式才能解决所请求的页面。
所以我确实需要在那个阶段就使用会话。

你曾经弄明白这个问题吗?我已经尝试了所有可能的解决方案:清除模块和处理程序、运行所有托管模块以处理所有请求、添加.aspx到结尾。无论如何,使用system.web.routing时,Session Start都不会触发。 - user559494
似乎Session在页面生命周期后加载,因此当IRouteHandler.GetHttpHandler启动时不可用。我必须做一些解决方法才能使我的场景正常工作,但不幸的是,直到在aspx页面(或其他IHttpHandler)中才能访问Session。 - Mathias Rönnlund
3个回答

8

我在这个答案中解释了这个问题的原因。现在我找到了一个解决方案!

  1. You create a custom HttpHandler class:

    class MyHttpHandler : IHttpHandler, IRequiresSessionState
    {
      public MyRequestHandler RequestHandler;
      public RequestContext Context;
      public MyHttpHandler(MyRequestHandler routeHandler, RequestContext context)
      {
        RequestHandler = routeHandler;
        Context = context;
      }
    
      public void ProcessRequest(HttpContext context)
      {
        throw new NotImplementedException();
      }
    
      public bool IsReusable
      {
        get { throw new NotImplementedException(); }
      }
    }
    

添加IRequiresSessionState接口很重要,否则IIS不会为此请求加载会话。我们不需要实现ProcessRequestIsReusable的逻辑,但类必须实现IHttpHandler接口。

  1. You change your RouteHandler implementation:

    public class MyRequestHandler : IRouteHandler
    {
      public IHttpHandler GetHttpHandler(RequestContext requestContext)
      {
          return new MyHttpHandler(this, requestContext);
      }
    
      public IHttpHandler DelayedGetHttpHandler(RequestContext requestContext)
      {
          // your custom routing logic comes here...
      }
    }
    
您只需将原始的会话依赖逻辑移动到DelayedGetHttpHandler函数中,在GetHttphandler函数中返回MyHttpHandler类的实例即可。
  1. Then, you hook your handling logic to the HttpApplication.PostAcquireRequestState event, e.g. in the Global.asax:

    public class Global : HttpApplication
    {
        public override void Init()
        {
            base.Init();
            PostAcquireRequestState += Global_PostAcquireRequestState;
        }
     }
    

更多参考,请查看此页面:https://msdn.microsoft.com/zh-cn/library/bb470252(v=vs.140).aspx。该页面解释了请求生命周期以及我为什么使用PostAcquireRequestState事件。

  1. In the event handler, you invoke your custom RouteHandling function:

    void Global_PostAcquireRequestState(object sender, EventArgs e)
    {
       if (HttpContext.Current.Handler is MyHttpHandler) {
         var handler = HttpContext.Current.Handler as MyHttpHandler;
         HttpContext.Current.Handler = handler.RouteHandler.DelayedGetHttpHandler(handler.Context);
       }
    }
    

就是这样。对我来说很有效。


这就是我寻找了8个小时的解决方案。我正在使用MVC 5,所有将模块添加到web.config并拥有自定义控制器处理程序的选项都无效。但是这个方法有效。不过,为什么不直接把逻辑放在ProcessRequest中,而不用操心任何global.asax——在第2步停止,并且不使用delayedhandler。 - Tod
哦,还有在MyHttpHandler中的“RouteHandler”应该改为“RequestHandler”。 - Tod
谢谢!太棒了!现在我可以根据用户登录情况为相同的URL提供不同的页面 ^_^。 - Alex
在我的情况下,MyHttpHandler 方法和属性在某些情况下被调用。为确保正常运行,我会移除 NotImplementedExceptions。 - Jorrit Schippers
这对我很有用,除了步骤1和2不是必需的。你只需要在Global.asax的Init()方法中订阅PostAcquireRequestState(步骤3),并在事件处理程序中直接对HttpContext.Current.Handler进行所有路由操作(步骤4),而无需创建自定义HttpHandler或RouteHandler。 - Manu
我需要在WebAPI中的区域内可用的会话......类似于这个概念,但是我遵循了:https://dev59.com/8ILba4cB1Zd3GeqPfofL#25371394,并且(要覆盖IRouteHandler),这与此处找到的示例非常相似.... https://dev59.com/92gu5IYBdhLWcg3wOUq-#11479021,然后在`public static Route MapHttpRoute(this AreaRegistrationContext context, string name, string routeTemplate, object defaults, object constraints)中添加了route.RouteHandler = new MyHttpControllerHandler();` - petrosmm

1

虽然我知道这是一个旧的线程,但如果像我一样的人遇到了同样的情况,我在这里提供答案here

你所要做的就是在web.config文件中的modules标签中添加一个runAllManagedModulesForAllRequests="true"属性,如下所示

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

然而,这并不是一个好的解决方案,因为它每次都会调用托管模块,我正在使用。

    <remove name="Session" />
    <add name="Session" type="System.Web.SessionState.SessionStateModule"/>

将它添加到 web.config 的模块部分,这是比之前更好的解决方案。


这个不起作用。HttpContext.Current.Session仍然为null。此外,如果某些代码是错误的或者不是解决方案(例如你的解决方案#1没有起作用),你应该将其从答案中删除或移动到底部,并在展示之前说一些类似“这不起作用”的话。 - toddmo

1

我不确定你是否能够这样做(虽然我可能错了)。我的记忆是IRequiresSessionState表示HttpHandler需要会话状态,而不是路由处理程序(负责为您提供适合路由的处理程序)。

你真的需要在路由处理程序本身中使用会话而不是它提供的处理程序吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接