请求在此上下文中不可用
我正在运行IIS 7集成模式,我越来越
请求在此上下文中不可用
当我尝试从Application_Start
调用的Log4Net相关函数中访问它时。 这是我的代码行
if (HttpContext.Current != null && HttpContext.Current.Request != null)
第二次比较正在抛出exception。
除了检查HttpContext.Current.Request为空之外,我还能检查什么?
当在iis7.5上执行runnig mvc时,发布了一个类似的问题@ 请求在此上下文exception中不可用
但是也没有相关的答案。
请参阅IIS7集成模式:请求在Application_Start中的上下文exception中不可用 :
“请求在此上下文中不可用”exception是在将ASP.NET应用程序移到IIS 7.0上的集成模式时可能会遇到的更常见的错误之一。 如果您尝试访问启动应用程序的请求的HttpContext,则在您的Global.asax文件的Application_Start方法的实现中发生此exception。
当你有自定义日志逻辑的时候,强迫要么不loggingapplication_start,要么不得不在logging器中发生exception(即使被处理),也是非常烦人的。
看起来,不是testingRequest
可用性,而是testingHandler
可用性:当没有Request
,仍然有一个请求处理程序会很奇怪。 Handler
testing不会引起这个可怕的Request is not available in this context
exception中Request is not available in this context
。
所以你可以改变你的代码:
var currContext = HttpContext.Current; if (currContext != null && currContext.Handler != null)
请注意,在http模块的上下文中,虽然Request
和Response
被定义(我已经看到在BeginRequest事件中),但是Handler
可能没有被定义。 所以如果你需要请求/响应日志logging在一个自定义http模块,我的答案可能不适合。
这是非常经典的情况:如果最终不得不检查由http实例提供的任何数据,请考虑在BeginRequest
事件下移动该代码。
void Application_BeginRequest(Object source, EventArgs e)
这是检查http标题,查询string等的正确位置。Application_Start适用于应用程序整个运行时间的设置,如路由,筛选器,日志logging等等。
请不要应用任何变通方法,例如静态.ctor或切换到经典模式,除非无法将代码从“ Start
移动到“ Start
请求。 这对绝大多数情况应该是可行的。
基于评论中解释的OP的详细需求,存在更合适的解决scheme。 OP指出,他希望在log4net中添加自定义数据,数据与请求相关。
log4net不是将每个log4net调用包装成一个自定义的集中式日志调用来处理检索请求相关的数据(每个日志调用),而是使用上下文字典来设置定制的附加数据来logging日志。 使用这些字典允许在BeginRequest事件中为当前请求定位请求日志数据,然后在EndRequest事件中解除它。 任何login都将受益于这些自定义数据。
而且,在请求上下文中不会发生的事情不会尝试logging与请求相关的数据,从而无需testing请求可用性。 这个解决scheme与Arman McHletan在他的回答中提出的原则相符。
为了使这个解决scheme起作用,您还需要在log4net appender上进行一些额外的configuration,以便他们logging您的自定义数据。
该解决scheme可以轻松实现为自定义日志增强模块。 这里是它的示例代码:
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>
并设置appenderslogging这些额外的属性,示例configuration:
<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>
由于在应用程序启动期间pipe道中没有请求上下文,所以我无法想象有什么办法猜测下一个实际请求可能会进入哪个服务器/端口。 你必须这样在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); }
是。 我也面临这个问题。 没有想到。 通过从“集成”模式进入“经典”模式,我能够解决这个问题。 感谢您在这里提供的build议。
您可以在不切换到经典模式的情况下解决问题,并仍然使用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 }
出于某种原因,静态types是通过HTTPContext中的请求创build的,允许您将其存储并立即在Application_Start事件中重用
您可以使用以下内容:
protected void Application_Start(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(new WaitCallback(StartMySystem)); } private void StartMySystem(object state) { Log(HttpContext.Current.Request.ToString()); }
在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在那里…
这个.Request基于一个标志故意抛出exception