La solicitud no está disponible en este contexto

Estoy ejecutando el modo integrado de IIS 7 y me estoy poniendo

La solicitud no está disponible en este contexto

cuando bash acceder a él en una función relacionada con Log4Net que se llama desde Application_Start . Esta es la línea de código que tengo

 if (HttpContext.Current != null && HttpContext.Current.Request != null) 

y se lanza una excepción para una segunda comparación.

¿Qué más puedo verificar que no sea comprobar HttpContext.Current.Request para null?


Se publica una pregunta similar @ La solicitud no está disponible en este contexto, excepción cuando runnig mvc en iis7.5

pero tampoco hay respuesta relevante allí.

Consulte el modo integrado de IIS7: La solicitud no está disponible en este contexto, excepción en Application_Start :

La excepción “La solicitud no está disponible en este contexto” es uno de los errores más comunes que puede recibir al mover aplicaciones ASP.NET al modo Integrado en IIS 7.0. Esta excepción ocurre en su implementación del método Application_Start en el archivo global.asax si intenta acceder al HttpContext de la solicitud que inició la aplicación.

Cuando tiene una lógica de registro personalizada, es bastante molesto forzar o no a iniciar application_start o tener que permitir que ocurra una excepción en el registrador (incluso si se maneja).

Parece que en lugar de probar la disponibilidad de Request , puede probar la disponibilidad del Handler : cuando no hay Request , sería extraño tener un controlador de solicitud. Y las pruebas para Handler no generan esa temida Request is not available in this context excepción de Request is not available in this context .

Entonces puedes cambiar tu código a:

 var currContext = HttpContext.Current; if (currContext != null && currContext.Handler != null) 

Tenga cuidado, en el contexto de un módulo http, Handler no puede definirse aunque se definan Request and Response (He visto eso en el evento BeginRequest). Entonces, si necesita un registro de solicitud / respuesta en un módulo http personalizado, mi respuesta puede no ser adecuada.

Este es un caso muy clásico: si termina teniendo que verificar los datos proporcionados por la instancia http, considere mover ese código bajo el evento BeginRequest .

 void Application_BeginRequest(Object source, EventArgs e) 

Este es el lugar correcto para buscar encabezados http, cadena de consulta y etc. … Application_Start es para la configuración que se aplica a la aplicación en tiempo de ejecución completo, como enrutamiento, filtros, registro, etc.

Por favor, no aplique ninguna solución alternativa , como static .ctor o cambiar al modo Classic a menos que no haya forma de mover el código de Start a BeginRequest . eso debería ser factible para la gran mayoría de sus casos.

Debido a que ya no hay contexto de Solicitud en el proceso durante el inicio de la aplicación, no puedo imaginar que haya alguna forma de adivinar en qué servidor / puerto podría aparecer la próxima solicitud real. Tienes que hacerlo en Begin_Session.

Esto es lo que estoy usando cuando no está en modo clásico. La sobrecarga es insignificante.

 ///  /// Class is called only on the first request ///  private class AppStart { static bool _init = false; private static Object _lock = new Object(); ///  /// Does nothing after first request ///  ///  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); } 

En función de las necesidades detalladas de OP explicadas en los comentarios, existe una solución más adecuada. El OP declara que desea agregar datos personalizados en sus registros con log4net, datos relacionados con las solicitudes.

En lugar de ajustar cada llamada de log4net a una llamada de registro centralizada personalizada que maneja la recuperación de datos relacionados con las solicitudes (en cada llamada de registro), log4net presenta diccionarios de contexto para configurar datos adicionales personalizados para el registro. El uso de estos dictionarios permite posicionar los datos de registro de solicitudes para la solicitud actual en el evento BeginRequest, y luego descartarlos en el evento EndRequest. Cualquier inicio de sesión se beneficiará de esos datos personalizados.

Y las cosas que no suceden en un contexto de solicitud no intentarán registrar datos relacionados con la solicitud, eliminando la necesidad de probar la disponibilidad de la solicitud. Esta solución coincide con el principio que Arman McHitaryan sugería en su respuesta .

Para que esta solución funcione, también necesitará alguna configuración adicional en sus apuntores log4net para que puedan registrar sus datos personalizados.

Esta solución se puede implementar fácilmente como un módulo de mejora de registro personalizado. Aquí hay un código de muestra:

 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(); } } } 

Añádalo a su sitio, muestra de IIS 7+ conf:

          

Y configure los appenders para registrar esas propiedades adicionales, configuración de muestra:

                                                       

Puede solucionar el problema sin cambiar al modo clásico y seguir utilizando 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 } 

Por alguna razón, el tipo estático se crea con una solicitud en su HTTPContext, lo que le permite almacenarlo y reutilizarlo inmediatamente en el evento Application_Start

Pude resolver este problema moviéndome al modo “Clásico” desde el modo “integrado”.

Puedes usar lo siguiente:

  protected void Application_Start(object sender, EventArgs e) { ThreadPool.QueueUserWorkItem(new WaitCallback(StartMySystem)); } private void StartMySystem(object state) { Log(HttpContext.Current.Request.ToString()); } 

haz esto en 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 + "/"); // ... } 

Funciona de maravilla. this.Context.Request está ahí …

this.Request lanza una excepción intencionalmente basada en una bandera