代码之家  ›  专栏  ›  技术社区  ›  Samuel Neff

HTTPContext.HideRequestResponse的内部解决方案?检测httpContext.Request是否真的可用?

  •  28
  • Samuel Neff  · 技术社区  · 14 年前

    我们正在迁移应用程序以使用iis7集成模式。在设计用于或不用于HTTP请求上下文的库代码中,我们通常具有如下代码:

    if (HttpContext.Current != null &&
        HttpContext.Current.Request != null) {
    
        // do something with HttpContext.Current.Request
    
    } else {
    
        // do equivalent thing without HttpContext..
    
    }
    

    但在IIS7集成模式下,检查 HttpContext.Current.Request 每当从调用此代码时引发异常 Application_Start .

    protected void Application_Start(object sender, EventArgs e)
    {
        SomeLibrary.DoSomethingWithHttpContextCurrentDetection();
    }
    

    结果:

    System.Web.httpException:请求在此上下文中不可用

    如何检测请求是否真正可用 不在异常处理程序中包装这些调用 并根据是否生成异常而采取行动。

    看着 HttpContext 在反射镜里我看到它有一个 internal bool HideRequestResponse 但它是内部的,所以我只能通过反射才能到达它,这很脆弱。有没有更正式/认可的方式来决定是否可以打电话 HttpContext.Request ?

    关于这个主题的博客文章说不要使用 请求上下文 但是,如何在通用库代码中确定是否可以使用 请求上下文 ?

    http://mvolo.com/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-applicationstart/

    我用的是上面提到的工作 Application_BeginRequest 和一个 initialized 字段只初始化一次,作为 BeginRequest 但是在每个调用应用程序中都必须这样做,而我更希望使库代码更健壮,并处理这种情况,不管从何处调用它。

    5 回复  |  直到 9 年前
        1
  •  5
  •   Kieren Johnstone    14 年前

    恐怕答案是你 不能 得到你想要的-微软认为这是一个“例外情况”,所以它会抛出一个例外。

    您可以像在回答中描述的那样使用反射,但您不想这样做,因此受到了微软提供的API的限制,无论是好是坏。

    如果你决定使用反射,注意的是 HttpApplication.InitInternal 方法设置HideRequestResponse标志。

    希望有帮助。我建议你向 Microsoft Connect .

        2
  •  9
  •   Tim Murphy    14 年前

    我将您的代码重构为:

    if (IsRequestAvailable())
    {
        // do something with HttpContext.Current.Request...
    }
    else
    {
        // do equivalent thing without HttpContext...
    }
    
    public Boolean IsRequestAvailable()
    {
        if (HttpContext.Current == null)
            return false;
    
        try
        {
            if (HttpContext.Current.Request == null)
                return false;
        }
        catch (System.Web.HttpException ex)
        {
            #if DEBUG
                // Testing exception to a magic string not the best practice but
                // it works for this demo.
                if (ex.Message == "Request is not available in this context")
                    return false;
    
                throw;
            #else
                return false;
            #endif
        }
    
        return true;
    }
    

    您的问题要求不要使用异常处理(我假设是出于性能原因),我的答案是。但是,通过将代码从使用“if”(httpcontext.current!=空&httpcontext.current.request!=null)“to”if(isRequestavailable())“当您找到如何不使用异常处理的答案时,只有一个地方可以更改代码。

        3
  •  4
  •   Jaroslav Jandek    14 年前

    不应该 即使 使用请求 (或响应) Application_Start 因为应用程序可以在没有请求的情况下启动。因此,将来当框架的其他部分停止提供请求对象时,您的应用程序甚至不会运行。

    如果你只是想暂时破解它,你可以使用反射(如果你有中等以上的信任)或者捕获一个异常(即使你不想这样做),并将结果存储在一个静态变量中,或者可能使用一个静态变量。 httpContext包装 以下内容:

    你也可以用 HttpRuntime.UsingIntegratedPipeline .

    因此,最好的方法是在初始化类或不在AppStart中初始化类时移除它们对httpContext的依赖。

    你在应用程序启动中使用请求的理由是什么?统计数据?或者只是告诉用户他唤醒了应用程序?

    编辑 用代码更好地解释:

    public static class ContextWrapper
    {
        public static HttpRequest Request
        {
            get
            {
                HttpContext context = HttpContext.Current;
                if (context == null) return null;
    
                if (HttpRuntime.UsingIntegratedPipeline)
                {
                    try { return context.Request; }
                    catch (HttpException e) { /* Consume or log e*/ return null; }
                    // Do not use message comparison - .NET translates messages for multi-culture environments.
                }
    
                return context.Request;
            }
        }
    }
    

    在代码中:

    if (ContextWrapper.Request != null) //...
    

    或者用户控制的更快的方法:

    public static class ContextWrapper2
    {
        public static bool IsIis7IntegratedAppStart { get; set; }
    
        public static HttpRequest Request
        {
            get
            {
                if (ContextWrapper2.IsIis7IntegratedAppStart) return null;
    
                HttpContext context = HttpContext.Current;
                if (context == null) return null;
    
                return context.Request;
            }
        }
    }
    

    应用程序内启动:

    protected void Application_Start(object sender, EventArgs e)
    {
        yourLibraryNamespace.ContextWrapper2.IsIis7IntegratedAppStart = true;
        //...
        yourLibraryNamespace.yourClass.Init();
        //...
        yourLibraryNamespace.ContextWrapper2.IsIis7IntegratedAppStart = false;
    }
    

    您可以在您的文档中注意到这种行为,并且应该一切正常。类AppStart的上下文应该是您获得此类异常的唯一位置。

    还可以在成员上实现IDisposable,并在AppStart中使用 using 这样你就不会忘记设置 IsIis7IntegratedAppStart = false .

        4
  •  1
  •   Matt Watson    9 年前

    我想我有解决办法。我维护了一个日志库,和您有相同的问题。如果是Web请求,我将从httpcontext获取一些数据。但是,根据日志库的使用方式,可能会发生相同的情况。这是我的解决方案。我的密钥修复程序正在检查处理程序是否为空。

    if (System.Web.Hosting.HostingEnvironment.IsHosted  
        && System.Web.HttpContext.Current != null 
        && System.Web.HttpContext.Current.Handler != null 
        && System.Web.HttpContext.Current.Request != null)
    {
        //access the Request object here
    }
    

    根据您试图完成的任务,您可能可以从system.web.hostingenvironment获取Web应用程序的一些属性和设置。

        5
  •  0
  •   Toby    14 年前

    我添加了一条评论,但它会自动隐藏。

    我认为更重要的是从请求中了解您需要什么。

    例如,您提供的提供解决方案的链接正在查找 Request.ApplicationPath .

    如果这正是你想要的(比如,加载web.config和app.config),你可以这样做:

            if (HttpRuntime.AppDomainAppId != null)
                return WebConfigurationManager.OpenWebConfiguration(HttpRuntime.AppDomainAppVirtualPath);
            else
                return ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    

    如果(或) HttpRuntime.ApplicationPath )这并不是你真正想要的,这将有助于了解 Request 实际上是在寻找。也许有更好、更安全的方法到达那里。