Подтвердить что ты не робот

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

Мы переносим приложение для использования интегрированного режима 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?

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

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

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

4b9b3361

Ответ 1

Я боюсь, что ответ: не может получить то, что вы хотите - Microsoft рассматривает этот случай как "исключительное обстоятельство", и поэтому он будет генерировать исключение.

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 5

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