Request is not available in this context
Asked Answered
R

13

132

I'm running IIS 7 Integrated mode and I'm getting

Request is not available in this context

when I try to access it in a Log4Net related function that is called from Application_Start. This is the line of code I've

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

and an exception is being thrown for second comparison.

What else can I check other than checking HttpContext.Current.Request for null??


A similar question is posted @ Request is not available in this context exception when runnig mvc on iis7.5

but no relevant answer there either.

Reflexive answered 25/3, 2010 at 17:48 Comment(1)
Would you guys recommend adding a try-catch block as my only option if I don't take the other two solutions as suggested in the link from Andrew Hare? like try { if (HttpContext.Current.Request.Headers["User_info"] != null) log4net.MDC.Set("UserInfo", HttpContext.Current.Request.Headers["User_info"].ToString()); } catch(){}Reflexive
M
88

Please see IIS7 Integrated mode: Request is not available in this context exception in Application_Start:

The “Request is not available in this context” exception is one of the more common errors you may receive on when moving ASP.NET applications to Integrated mode on IIS 7.0. This exception happens in your implementation of the Application_Start method in the global.asax file if you attempt to access the HttpContext of the request that started the application.

Mohammed answered 25/3, 2010 at 17:50 Comment(5)
More discussion of this situation here: #1790957Shwalb
Thanks. I had seen that link before. It says: "Basically, if you happen to be accessing the request context in Application_Start, you have two choices: 1) Change your application code to not use the request context (recommended). 2) Move the application to Classic mode (NOT recommended)." Are their no other options? My logging code writes stuff in DB e.g. Application started, if not via a request than those fields should be set to null rather than completely removing my log statement.Reflexive
I've got the same sort of logging requirement, if the context is available use it to populate the database, if not leave the fields null. (In my case, don't write a record to one logging table, but it would help if there were a good way to determine whether or not is available.)Dunant
Didn't like it, but wrapping the check in a try-catch was the only option other than a major refactoring of our logging code (and/or entire app)Dunant
Is there any way to tell if you're in a situation where the request will not be available? Some property of the HttpContext that knows about this? Why does it throw an exception instead of just returning Nothing, like many of the other properties?Logistics
M
69

When you have custom logging logic, it is rather annoying to be forced either not to log application_start or to have to let an exception occurs in the logger (even if handled).

It appears that rather than testing for Request availability, you can test for Handler availability: when there is no Request, it would be strange to still have a request handler. And testing for Handler does not raise that dreaded Request is not available in this context exception.

So you may change your code to:

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

Beware, in the context of a http module, Handler may not be defined though Request and Response are defined (I have seen that in BeginRequest event). So if you need request/response logging in a custom http module, my answer may not be suitable.

Malraux answered 4/4, 2014 at 9:17 Comment(3)
Moreover the drawbacks already stated here, I realized it was really not the way to go for the specific needs explained by the OP in a comment. See my other answer on this page.Enswathe
This did the trick for me, I just needed to check the Request object without it throwing an exception. TyAmati
This is actually a great solution to a stupid problem. Whoever decided that calling the getter for context.request should throw an exception instead of returning null should be flogged.Marylnmarylou
S
19

This is very classic case: If you end up having to check for any data provided by the http instance then consider moving that code under the BeginRequest event.

void Application_BeginRequest(Object source, EventArgs e)

This is the right place to check for http headers, query string and etc... Application_Start is for the settings that apply for the application entire run time, such as routing, filters, logging and so on.

Please, don't apply any workarounds such as static .ctor or switching to the Classic mode unless there's no way to move the code from the Start to BeginRequest. that should be doable for the vast majority of your cases.

Sirenasirenic answered 13/5, 2014 at 14:8 Comment(0)
G
7

Since there's no Request context in the pipeline during app start anymore, I can't imagine there's any way to guess what server/port the next actual request might come in on. You have to so it on Begin_Session.

Here's what I'm using when not in Classic Mode. The overhead is negligible.

/// <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);
}
Grits answered 18/5, 2010 at 5:2 Comment(1)
Thanks, this got my site up and running again after it suddenly came down with this symptom. Strangely enough, I had not changed from classic ASP.NET in the app pool -- I still got the error. Adding a variant of this code (using Interlocked.Exchange(ref int, int)) solved the problem.Landward
M
6

Based on OP detailed needs explained in comments, a more appropriate solution exists. The OP states he wishes to add custom data in its logs with log4net, data related to requests.

Rather than wrapping each log4net call into a custom centralized log call which handles retrieving request related data (on each log call), log4net features context dictionaries for setting up custom additional data to log. Using those dictionnaries allows to position your request log data for current request at BeginRequest event, then to dismiss it at EndRequest event. Any log in between will benefit from these custom data.

And things that do not happen in a request context will not try to log request related data, eliminating the need to test for request availability. This solution matches the principle Arman McHitaryan was suggesting in his answer.

For this solution to work, you will also need some additional configuration on your log4net appenders in order for them to log your custom data.

This solution can be easily implemented as a custom log enhancement module. Here is some sample code for it:

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

Add it to your site, IIS 7+ configuration sample:

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

And set up appenders to log those additional properties, sample config:

<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>
Malraux answered 28/5, 2014 at 9:43 Comment(1)
+1 for pointing out that you can use HttpContext.Current.Items["IP"] instead of HttpContext.Request.UserHostAddress. In case of empty Request this works for fetching data - which saved me :) thanks.Connotative
P
2

Set application pool to .NET v4.5 Classic

Penuchle answered 27/9, 2022 at 3:7 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Mong
B
1

I was able to workaround/hack this problem by moving in to "Classic" mode from "integrated" mode.

Bissextile answered 30/4, 2012 at 8:12 Comment(0)
M
1

You can get around the problem without switching to classic mode and still use 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
   }

For some reason, the static type is created with a request in its HTTPContext, allowing you to store it and reuse it immediately in the Application_Start event

Mexico answered 8/2, 2014 at 14:59 Comment(2)
I dunno.. Running locally it seems that it doesn't "see" the port when I try to use: initialRequest.Url.GetLeftPart(UriPartial.Authority); Will have to look for a different way.Provitamin
Terribly hackish, but may help in some desperate cases. (I am a bit balanced between down-voting or up-voting this, so I just do not cast vote.)Enswathe
A
0

This worked for me - if you have to log in Application_Start, do it before you modify the context. You will get a log entry, just with no source, like:

2019-03-12 09:35:43,659 INFO (null) - Application Started

I generally log both the Application_Start and Session_Start, so I see more detail in the next message

2019-03-12 09:35:45,064 INFO ~/Leads/Leads.aspx - Session Started (Local)

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


Aguste answered 12/3, 2019 at 13:50 Comment(0)
C
0

In visual studio 2012, When I published the solution mistakenly with 'debug' option I got this exception. With 'release' option it never occurred. Hope it helps.

Cardin answered 22/5, 2019 at 6:53 Comment(0)
A
-1

public bool StartVideo(byte channel) { try { CommandObject command = new CommandObject(Commands.START_VIDEO, new byte[] {channel}, channel);

            m_ResponseEvent.Reset();

            lock (m_Commands)
            {
                m_Commands.Enqueue(command);
            }

            if (m_ResponseEvent.WaitOne(5000, true))
            {
                return m_Response == null ? false : true;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        return false;
    }
Ayr answered 12/3, 2022 at 11:40 Comment(1)
Welcome to Stack Overflow. Code is a lot more helpful when it is accompanied by an explanation. Stack Overflow is about learning, not providing snippets to blindly copy and paste. Please edit your question and explain how it answers the specific question being asked. See How to Answer.Ode
F
-3

You can use following:

    protected void Application_Start(object sender, EventArgs e)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(StartMySystem));
    }

    private void StartMySystem(object state)
    {
        Log(HttpContext.Current.Request.ToString());
    }
Flatwise answered 17/10, 2011 at 20:26 Comment(0)
P
-6

do this in 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 + "/");
  // ...
}

works like a charm. this.Context.Request is there...

this.Request throws exception intentionally based on a flag

Pinchas answered 17/8, 2010 at 6:58 Comment(2)
-1: Read the question: this is what is failing (with IIS >=7 and Integrated mode)Target
This is what happens when pirates loose their job and try themselves in programming :) No offense, Man ;)Sirenasirenic

© 2022 - 2024 — McMap. All rights reserved.