Запрос в этом контексте недоступен



Я запускаю интегрированный режим IIS 7, и я получаю



запрос недоступен в этом контексте



когда я пытаюсь получить доступ к нему в функции, связанной с Log4Net, которая вызывается из Application_Start. Это строка кода у меня



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


и исключение создается для второго сравнения.



что еще я могу проверить, кроме проверки HttpContext.Текущий.Запрос на null??





аналогичный вопрос опубликовано @
Запрос не доступен в этом контексте исключение, когда runnig mvc на iis7. 5



но и там нет соответствующего ответа.

637   9  

9 ответов:

см. интегрированный режим IIS7: запрос не доступен в этом контексте исключение в Application_Start:

"запрос не доступен в этом контексте" исключение является одним из более распространенные ошибки, которые вы можете получить, когда перемещение ASP.NET приложения к Интегрированный режим на IIS 7.0. Этот исключение происходит в вашем осуществление Метод Application_Start в глобальный.файл asax, если вы попытаетесь доступ к HttpContext запрос это запустило приложение.

когда у вас есть пользовательская логика ведения журнала, это довольно раздражает, чтобы быть вынужденным либо не регистрировать 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, мой ответ может не подойти.

Это очень классический случай: если вам придется проверять любые данные, предоставленные экземпляром http, то подумайте о перемещении этого кода под BeginRequest событие.

void Application_BeginRequest(Object source, EventArgs e)

Это подходящее место для проверки заголовков http, строки запроса и т. д... Application_Start для параметров, которые применяются для всего времени выполнения приложения, таких как маршрутизация, фильтры, ведение журнала и так далее.

пожалуйста, не применяйте никаких обходных путей например, статические .ctor или переключение на классический режим, если нет никакого способа, чтобы переместить код из Start до BeginRequest. что должно быть выполнимо для подавляющего большинства ваших дел.

поскольку во время запуска приложения в конвейере больше нет контекста запроса, я не могу представить, что можно догадаться, на каком сервере/Порту может появиться следующий фактический запрос. Вы должны так это на 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);
}

на основе подробных потребностей OP, объясненных в комментариях, существует более подходящее решение. ОП заявляет, что он хочет добавить пользовательские данные в свои журналы с 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>

и настроить приложения для регистрации этих дополнительных свойств, пример конфигурации:

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

вы можете обойти проблему без перехода в классический режим и по-прежнему использовать 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

Я смог решить эту проблему, перейдя в" классический "режим из" интегрированного " режима.

вы можете использовать следующие:

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

    private void StartMySystem(object state)
    {
        Log(HttpContext.Current.Request.ToString());
    }

сделать это в глобальном.асакс.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 + "/");
  // ...
}

работает как шарм. этот.Контекст.Запрос есть...

этот.Запрос выдает исключение намеренно на основе флага

Comments

    Ничего не найдено.