Мы мигрируем, приложение для использования 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
?
Я использую обходное решение, упомянул там, который должен использовать Application_BeginRequest
и initialized
поле, чтобы только инициализировать однажды как часть BeginRequest
, но это должно быть сделано в каждом вызывающем приложении, тогда как я предпочел бы заставлять библиотеку кодировать более устойчивый и обработать эту ситуацию независимо от того, откуда это называют.
Боюсь, ответ состоит в том, что вы не можете получить то, что хотите - Microsoft рассматривает этот случай как «исключительное обстоятельство» и поэтому вызовет исключение.
Вы можете использовать отражение, как вы описываете в своем ответе, но вы этого не хотите, и поэтому вы ограничены API, предоставленным Microsoft, к лучшему или к худшему.
Если вы все же решите использовать отражение, обратите внимание на метод HttpApplication.InitInternal
, который устанавливает флаг HideRequestResponse.
Надеюсь, что это поможет. Я предлагаю вам отправить отчет через Microsoft Connect .
Я бы рефакторизовал ваш код следующим образом:
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())", у вас есть только одно место, чтобы изменить код, когда вы найдете ответ, как не использовать обработку исключений.
Вы не должны даже использовать запрос (или ответ) в 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
.
Я добавил комментарий, но он автоматически скрывается.
Я думаю, что важнее иметь представление о том, что именно вам нужно от запроса.
Например, в приведенной вами ссылке, которая предлагает обходной путь, ищется Request.ApplicationPath
.
Если это действительно то, что вам нужно (скажем, для загрузки web.config по сравнению с app.config), вы можете сделать так:
if (HttpRuntime.AppDomainAppId != null)
return WebConfigurationManager.OpenWebConfiguration(HttpRuntime.AppDomainAppVirtualPath);
else
return ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
Если это (или HttpRuntime.ApplicationPath
) не то, что вам нужно, было бы полезно знать, какие свойства Request
вы ищете. Возможно, есть лучший, более безопасный способ добиться этого.