Запрос в этом контексте недоступен
Я запускаю интегрированный режим IIS 7, и я получаю
Запрос не доступен в этом контексте
когда я пытаюсь получить к нему доступ в связанной с Log4Net функции, вызываемой из Application_Start
. Это строка кода, в которой я
if (HttpContext.Current != null && HttpContext.Current.Request != null)
и исключение вызывается для второго сравнения.
Что еще я могу проверить, кроме проверки HttpContext.Current.Request для null?
Аналогичный вопрос отправлен @
Запрос не доступен в этом контексте при запуске runnig mvc на iis7.5
но нет соответствующего ответа.
Ответы
Ответ 1
См. Интегрированный режим IIS7: Запрос в этом контексте не доступен в Application_Start:
"Запрос недоступен в этом контекст" является одним из наиболее общие ошибки, которые вы можете получить, когда перемещение приложений ASP.NET Интегрированный режим на IIS 7.0. Эта исключение происходит в вашем реализации Метод Application_Start в global.asax, если вы пытаетесь доступ к HttpContext запроса который запустил приложение.
Ответ 2
Когда у вас есть собственная логика ведения журнала, довольно раздражает необходимость принудительно не регистрировать application_start или разрешать возникновение исключений в регистраторе (даже если они обрабатываются).
Похоже, что вместо проверки доступности Request
можно проверить доступность Handler
: когда нет Request
, было бы странно иметь обработчик запросов. И тестирование на Handler
не вызывает этого страшного исключения Request is not available in this context
.
Таким образом, вы можете изменить свой код на:
var currContext = HttpContext.Current;
if (currContext != null && currContext.Handler != null)
Осторожно, в контексте модуля http Handler
может не определяться, хотя Request
и Response
определены (я видел это в событии BeginRequest). Поэтому, если вам нужна регистрация запросов/ответов в пользовательском http-модуле, мой ответ может не подойти.
Ответ 3
Это очень классический случай: если вам нужно проверить любые данные, предоставленные экземпляром http, тогда подумайте о перемещении этого кода в BeginRequest
.
void Application_BeginRequest(Object source, EventArgs e)
Это подходящее место для проверки заголовков HTTP, строки запроса и т.д....
Application_Start
- для параметров, которые применяются для всего времени выполнения приложения, такого как маршрутизация, фильтры, ведение журнала и т.д.
Пожалуйста, не применяйте обходные методы, такие как static.ctor или переключение в классический режим, если нет способа переместить код с Start
на BeginRequest
. это должно быть выполнимо для подавляющего большинства ваших случаев.
Ответ 4
Поскольку в начале приложения нет контекста запроса в конвейере, я не могу представить, чтобы какой-либо способ угадать, к какому серверу/порту может прийти следующий фактический запрос. Вы должны это сделать на Begin_Session.
Вот что я использую, когда не в классическом режиме. Накладные расходы незначительны.
/// <summary>
/// Class is called only on the first request
/// </summary>
private class AppStart
{
static bool _init = false;
private static Object _lock = new Object();
/// <summary>
/// Does nothing after first request
/// </summary>
/// <param name="context"></param>
public static void Start(HttpContext context)
{
if (_init)
{
return;
}
//create class level lock in case multiple sessions start simultaneously
lock (_lock)
{
if (!_init)
{
string server = context.Request.ServerVariables["SERVER_NAME"];
string port = context.Request.ServerVariables["SERVER_PORT"];
HttpRuntime.Cache.Insert("basePath", "http://" + server + ":" + port + "/");
_init = true;
}
}
}
}
protected void Session_Start(object sender, EventArgs e)
{
//initializes Cache on first request
AppStart.Start(HttpContext.Current);
}
Ответ 5
На основании подробных потребностей ОП, описанных в комментариях, существует более подходящее решение.
ОП заявляет, что хочет добавить пользовательские данные в свои журналы с помощью log4net, данные, относящиеся к запросам.
Вместо того, чтобы заключать каждый вызов log4net в настраиваемый централизованный вызов журнала, который обрабатывает получение данных, связанных с запросом (при каждом вызове журнала), log4net имеет контекстные словари для настройки пользовательских дополнительных данных для регистрации. Использование этих словарей позволяет расположить данные журнала запросов для текущего запроса в событии BeginRequest, а затем отклонить их в событии EndRequest. Любые входы между ними получат выгоду от этих пользовательских данных.
А вещи, которые не происходят в контексте запроса, не будут пытаться регистрировать данные, относящиеся к запросу, устраняя необходимость проверки доступности запроса. Это решение соответствует принципу, предложенному Арманом Макхитаряном в его ответе.
Чтобы это решение работало, вам также понадобятся дополнительные настройки в ваших приложениях log4net, чтобы они могли регистрировать ваши пользовательские данные.
Это решение может быть легко реализовано в виде пользовательского модуля расширения журнала. Вот пример кода для этого:
using System;
using System.Web;
using log4net;
using log4net.Core;
namespace YourNameSpace
{
public class LogHttpModule : IHttpModule
{
public void Dispose()
{
// nothing to free
}
private const string _ipKey = "IP";
private const string _urlKey = "URL";
private const string _refererKey = "Referer";
private const string _userAgentKey = "UserAgent";
private const string _userNameKey = "userName";
public void Init(HttpApplication context)
{
context.BeginRequest += WebAppli_BeginRequest;
context.PostAuthenticateRequest += WebAppli_PostAuthenticateRequest;
// All custom properties must be initialized, otherwise log4net will not get
// them from HttpContext.
InitValueProviders(_ipKey, _urlKey, _refererKey, _userAgentKey,
_userNameKey);
}
private void InitValueProviders(params string[] valueKeys)
{
if (valueKeys == null)
return;
foreach(var key in valueKeys)
{
GlobalContext.Properties[key] = new HttpContextValueProvider(key);
}
}
private void WebAppli_BeginRequest(object sender, EventArgs e)
{
var currContext = HttpContext.Current;
currContext.Items[_ipKey] = currContext.Request.UserHostAddress;
currContext.Items[_urlKey] = currContext.Request.Url.AbsoluteUri;
currContext.Items[_refererKey] = currContext.Request.UrlReferrer != null ?
currContext.Request.UrlReferrer.AbsoluteUri : null;
currContext.Items[_userAgentKey] = currContext.Request.UserAgent;
}
private void WebAppli_PostAuthenticateRequest(object sender, EventArgs e)
{
var currContext = HttpContext.Current;
// log4net doc states that %identity is "extremely slow":
// http://logging.apache.org/log4net/release/sdk/log4net.Layout.PatternLayout.html
// So here is some custom retrieval logic for it, so bad, especialy since I
// tend to think this is a missed copy/paste in that documentation.
// Indeed, we can find by inspection in default properties fetch by log4net a
// log4net:Identity property with the data, but it looks undocumented...
currContext.Items[_userNameKey] = currContext.User.Identity.Name;
}
}
// General idea coming from
// http://piers7.blogspot.fr/2005/12/log4net-context-problems-with-aspnet.html
// We can not use log4net ThreadContext or LogicalThreadContext with asp.net, since
// asp.net may switch thread while serving a request, and reset the call context
// in the process.
public class HttpContextValueProvider : IFixingRequired
{
private string _contextKey;
public HttpContextValueProvider(string contextKey)
{
_contextKey = contextKey;
}
public override string ToString()
{
var currContext = HttpContext.Current;
if (currContext == null)
return null;
var value = currContext.Items[_contextKey];
if (value == null)
return null;
return value.ToString();
}
object IFixingRequired.GetFixedObject()
{
return ToString();
}
}
}
Добавьте его на свой сайт, пример IIS 7+ conf:
<system.webServer>
<!-- other stuff removed ... -->
<modules>
<!-- other stuff removed ... -->
<add name="LogEnhancer" type="YourNameSpace.LogHttpModule, YourAssemblyName" preCondition="managedHandler" />
<!-- other stuff removed ... -->
</modules>
<!-- other stuff removed ... -->
</system.webServer>
И настройте appenders для регистрации этих дополнительных свойств, пример конфигурации:
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<!-- other stuff removed ... -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger - %message - %property%newline%exception" />
</layout>
</appender>
<appender name="SqlAppender" type="log4net.Appender.AdoNetAppender">
<!-- other stuff removed ... -->
<commandText value="INSERT INTO YourLogTable ([Date],[Thread],[Level],[Logger],[UserName],[Message],[Exception],[Ip],[Url],[Referer],[UserAgent]) VALUES (@log_date, @thread, @log_level, @logger, @userName, @message, @exception, @Ip, @Url, @Referer, @UserAgent)" />
<!-- other parameters removed ... -->
<parameter>
<parameterName value="@userName" />
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{userName}" />
</layout>
</parameter>
<parameter>
<parameterName value="@Ip"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Ip}" />
</layout>
</parameter>
<parameter>
<parameterName value="@Url"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Url}" />
</layout>
</parameter>
<parameter>
<parameterName value="@Referer"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{Referer}" />
</layout>
</parameter>
<parameter>
<parameterName value="@UserAgent"/>
<dbType value="String" />
<size value="255" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{UserAgent}" />
</layout>
</parameter>
</appender>
<!-- other stuff removed ... -->
</log4net>
Ответ 6
Вы можете обойти проблему без перехода в классический режим и использовать Application_Start
public class Global : HttpApplication
{
private static HttpRequest initialRequest;
static Global()
{
initialRequest = HttpContext.Current.Request;
}
void Application_Start(object sender, EventArgs e)
{
//access the initial request here
}
По какой-то причине статический тип создается с запросом в его HTTPContext, позволяя вам его хранить и повторно использовать сразу в событии Application_Start
Ответ 7
Я смог обойти/взломать эту проблему, перейдя в "классический" режим из "интегрированного" режима.
Ответ 8
Это сработало для меня - если вам нужно войти в Application_Start, сделайте это до изменения контекста. Вы получите запись в журнале, просто без источника, например:
2019-03-12 09: 35: 43,659 INFO (null) - Приложение запущено
Я обычно регистрирую и Application_Start, и Session_Start, так что я вижу больше подробностей в следующем сообщении
2019-03-12 09: 35: 45,064 INFO ~/Leads/Leads.aspx - сессия запущена (локально)
protected void Application_Start(object sender, EventArgs e)
{
log4net.Config.XmlConfigurator.Configure();
log.Info("Application Started");
GlobalContext.Properties["page"] = new GetCurrentPage();
}
protected void Session_Start(object sender, EventArgs e)
{
Globals._Environment = WebAppConfig.getEnvironment(Request.Url.AbsoluteUri, Properties.Settings.Default.LocalOverride);
log.Info(string.Format("Session Started ({0})", Globals._Environment));
}
Ответ 9
В Visual Studio 2012, когда я по ошибке опубликовал решение с опцией 'debug', я получил это исключение. С опцией "релиз" это никогда не происходило. Надеюсь, поможет.
Ответ 10
Вы можете использовать следующее:
protected void Application_Start(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(StartMySystem));
}
private void StartMySystem(object state)
{
Log(HttpContext.Current.Request.ToString());
}
Ответ 11
сделайте это в файле global.asax.cs:
protected void Application_Start()
{
//string ServerSoftware = Context.Request.ServerVariables["SERVER_SOFTWARE"];
string server = Context.Request.ServerVariables["SERVER_NAME"];
string port = Context.Request.ServerVariables["SERVER_PORT"];
HttpRuntime.Cache.Insert("basePath", "http://" + server + ":" + port + "/");
// ...
}
работает как шарм. this.Context.Request есть...
this.Request выбрасывает исключение намеренно на основе флага