Обходной путь для HttpContext.HideRequestResponse, являющегося внутренним? Определить, действительно ли HttpContext.Request доступен? - PullRequest
28 голосов
/ 09 апреля 2010

Мы переносим приложение в интегрированный режим 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 в Reflector, я вижу, что у него есть поле internal bool HideRequestResponse, но оно внутреннее, поэтому я могу получить к нему только отражение, и оно хрупкое. Есть ли более официальный / одобренный способ определить, можно ли звонить HttpContext.Request?

В этом посте на эту тему говорится, что не следует использовать HttpContext, но как в общем коде библиотеки вы можете определить, можно ли использовать HttpContext?

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

Я использую обходной путь, упомянутый там, который должен использовать Application_BeginRequest и поле initialized для инициализации только один раз как часть BeginRequest, но это должно быть сделано в каждом вызывающем приложении, тогда как я ' я предпочел бы сделать код библиотеки более надежным и обрабатывать эту ситуацию независимо от того, откуда он вызывается.

Ответы [ 5 ]

9 голосов
/ 05 июля 2010

Я бы реорганизовал ваш код так:

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! = Null && HttpContext.Current.Request! = Null)» на «If (IsRequestAvailable ())», у вас есть только одно место для изменения кода, когда вы найдете ответьте, как не использовать обработку исключений.

5 голосов
/ 06 июля 2010

Боюсь, ответ в том, что вы не можете получить то, что вы хотите - Microsoft рассматривает это дело как «исключительное обстоятельство», и поэтому оно выдаст исключение.

Вы можете использовать рефлексию, как вы описываете в своем ответе, но вы не хотите, и поэтому ограничены API, предоставленным Microsoft, к лучшему или к худшему.

Если вы решили использовать рефлексию, обратите внимание на метод HttpApplication.InitInternal, который устанавливает флаг HideRequestResponse.

Надеюсь, это поможет. Я бы предложил вам отправить отчет в Microsoft Connect .

4 голосов
/ 05 июля 2010

Вы не должны даже использовать запрос (или ответ) в Application_Start, поскольку приложение может быть запущено без запроса.Так что в будущем ваше приложение не будет работать даже тогда, когда другие части инфраструктуры прекратят предоставлять объект Request.

Если вы хотите просто временно взломать его, вы можете использовать Reflection (если у вас доверие выше среднего)или перехват исключения (даже если вы этого не хотите) и сохранение результата в статической переменной или, возможно, использование статической оболочки HttpContext :

Также вы можете использовать HttpRuntime.UsingIntegratedPipeline.

Таким образом, лучший подход - это удалить зависимость ваших классов от HttpContext, когда они инициализируются, или не инициализировать их в appstart.

Каков ваш смысл использовать Request в запуске приложения в любом случае?Для статистики?Или просто сказать пользователю, что он разбудил приложение?

Отредактировано с кодом, чтобы объяснить лучше:

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.

1 голос
/ 24 июля 2015

Я думаю, у меня есть решение для вас. Я поддерживаю библиотеку журналов и имею ту же проблему, что и вы. Если это веб-запрос, я беру некоторые данные из 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.Hosting.HostingEnvironment

0 голосов
/ 08 июля 2010

Я добавил комментарий, но он автоматически скрыт.

Я думаю, что более важно иметь представление о том, что вам нужно из запроса.

Например, указанная вами ссылка, которая предоставляет обходной путь, ищет Request.ApplicationPath.

Если это действительно то, что вы ищете (например, для загрузки web.config против app.config), вы можете сделать это:

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

Если это (или HttpRuntime.ApplicationPath) не то, что вы на самом деле ищете, было бы полезно узнать, какие свойства Request вы действительно ищете . Может быть, есть лучший, более безопасный способ добраться туда.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...