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