Verzoek is in deze context niet beschikbaar

Ik gebruik de geïntegreerde modus van IIS 7 en ik krijg

Verzoek is in deze context niet beschikbaar

wanneer ik toegang probeer te krijgen in een Log4Net-gerelateerde functie die wordt aangeroepen vanuit Application_Start. Dit is de regel code die ik heb

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

en er wordt een uitzondering gegenereerd voor de tweede vergelijking.

Wat kan ik nog meer controleren dan het controleren van HttpContext.Current.Request for null??


Een soortgelijke vraag is geplaatst @
Verzoek is niet beschikbaar in deze context uitzondering bij het uitvoeren van mvc op iis7.5

maar ook daar geen relevant antwoord.


Antwoord 1, autoriteit 100%

Zie IIS7 Geïntegreerde modus: Verzoek is niet beschikbaar in deze context uitzondering in Application_Start:

De “Verzoek is niet beschikbaar in deze”
context” uitzondering is een van de meer
veelvoorkomende fouten die u kunt ontvangen op wanneer:
ASP.NET-toepassingen verplaatsen naar
Geïntegreerde modus op IIS 7.0. Deze
uitzondering gebeurt in uw
implementatie van de
Application_Start methode in de
global.asax-bestand als u probeert om
toegang krijgen tot de HttpContext van het verzoek
waarmee de applicatie is gestart.


Antwoord 2, autoriteit 75%

Als je aangepaste loglogica hebt, is het nogal vervelend om te worden gedwongen om application_start niet te loggen of om een ​​uitzondering te moeten toestaan ​​in de logger (zelfs als deze wordt afgehandeld).

Het lijkt erop dat u in plaats van te testen op beschikbaarheid van Request, kunt testen op beschikbaarheid van Handler: als er geen Requestis, zou het vreemd om nog steeds een verzoekbehandelaar te hebben. En testen voor Handlerlevert die gevreesde uitzondering Request is not available in this contextniet op.

U kunt uw code dus wijzigen in:

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

Pas op, in de context van een http-module is het mogelijk dat Handlerniet is gedefinieerd hoewel Requesten Responsewel zijn gedefinieerd (ik heb gezien dat in BeginRequest-gebeurtenis). Dus als je het loggen van verzoeken/antwoorden in een aangepaste http-module nodig hebt, is mijn antwoord mogelijk niet geschikt.


Antwoord 3, autoriteit 20%

Dit is een heel klassiek geval: als u uiteindelijk moet controleren op gegevens die worden geleverd door de http-instantie, overweeg dan om die code te verplaatsen naar de gebeurtenis BeginRequest.

void Application_BeginRequest(Object source, EventArgs e)

Dit is de juiste plaats om te controleren op http-headers, querystrings en dergelijke…
Application_Startis voor de instellingen die gedurende de gehele runtime van de applicatie van toepassing zijn, zoals routering, filters, logging enzovoort.

Gebruik geen tijdelijke oplossingenzoals statische .ctor of schakel over naar de klassieke modus, tenzij er geen manier is om de code van de Startnaar BeginRequest. dat zou in de overgrote meerderheid van uw gevallen mogelijk moeten zijn.


Antwoord 4, autoriteit 8%

Omdat er geen verzoekcontext meer in de pijplijn zit tijdens het starten van de app, kan ik me niet voorstellen dat er een manier is om te raden op welke server/poort het volgende daadwerkelijke verzoek binnenkomt. Je moet het zo doen op Begin_Session.

Dit is wat ik gebruik als ik niet in de klassieke modus ben. De overhead is verwaarloosbaar.

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

Antwoord 5, autoriteit 7%

Op basis van de gedetailleerde OP-behoeften die worden uitgelegd in opmerkingen, bestaat er een geschiktere oplossing .
De OP zegt dat hij aangepaste gegevens in zijn logs wil toevoegen met log4net, gegevens met betrekking tot verzoeken.

In plaats van elke log4net-oproep in een aangepaste gecentraliseerde log-oproep te stoppen die het ophalen van verzoekgerelateerde gegevens (bij elke log-oproep) afhandelt, beschikt log4net over contextwoordenboeken voor het instellen van aangepaste aanvullende gegevens om te loggen. Door deze woordenboeken te gebruiken, kunt u uw verzoeklogboekgegevens voor het huidige verzoek positioneren bij de BeginRequest-gebeurtenis en deze vervolgens afwijzen bij de EndRequest-gebeurtenis. Elke log-in tussen zal profiteren van deze aangepaste gegevens.

En dingen die niet in een verzoekcontext gebeuren, zullen niet proberen om verzoekgerelateerde gegevens te loggen, waardoor het niet nodig is om te testen op de beschikbaarheid van verzoeken. Deze oplossing komt overeen met het principe dat Arman McHitaryan suggereerde in zijn antwoord.

Om deze oplossing te laten werken, heeft u ook wat extra configuratie nodig op uw log4net-appenders zodat ze uw aangepaste gegevens kunnen loggen.

Deze oplossing kan eenvoudig worden geïmplementeerd als een aangepaste module voor logboekverbetering. Hier is wat voorbeeldcode voor:

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

Voeg het toe aan uw site, IIS 7+ configuratievoorbeeld:

<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>

En stel appenders in om die extra eigenschappen te loggen, voorbeeldconfiguratie:

<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

In Visual Studio 2012, toen ik de oplossing verkeerd heeft gepubliceerd met ‘Debug’-optie heb ik deze uitzondering. Met de optie ‘Release’ heeft het nooit opgetreden. Hoop dat het helpt.

Other episodes