Обходное решение для HttpContext. Причем HideRequestResponse внутренний? Обнаружьте если HttpContext. Запрос действительно доступен?

Мы мигрируем, приложение для использования 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();
}

Результаты в:

Система. Сеть. HttpException: Запрос не доступен в этом контексте

Как я могу обнаружить, действительно ли запрос доступен, не перенося эти вызовы в обработчик исключений и принимая меры на основе того, сгенерировано ли исключение или нет.

Взгляд на HttpContext в Отражателе я вижу, что он имеет 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, но это должно быть сделано в каждом вызывающем приложении, тогда как я предпочел бы заставлять библиотеку кодировать более устойчивый и обработать эту ситуацию независимо от того, откуда это называют.

28
задан Samuel Neff 3 February 2016 в 17:18
поделиться

4 ответа

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

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

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

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

5
ответ дан 28 November 2019 в 03:53
поделиться

Я бы рефакторизовал ваш код следующим образом:

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())", у вас есть только одно место, чтобы изменить код, когда вы найдете ответ, как не использовать обработку исключений.

10
ответ дан 28 November 2019 в 03:53
поделиться

Вы не должны даже использовать запрос (или ответ) в 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 .

4
ответ дан 28 November 2019 в 03:53
поделиться

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

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

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

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

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

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

0
ответ дан 28 November 2019 в 03:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: