[asp.net] 이 컨텍스트에서는 요청을 사용할 수 없습니다.

IIS 7 통합 모드를 실행하고 있는데

이 컨텍스트에서는 요청을 사용할 수 없습니다.

에서 호출되는 Log4Net 관련 함수에서 액세스하려고 할 때 Application_Start. 이것은 내가 가진 코드 줄입니다.

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

두 번째 비교를 위해 예외가 발생합니다.

HttpContext.Current.Request가 null인지 확인하는 것 외에 다른 무엇을 확인할 수 있습니까 ??


유사한 질문이 게시 됨 @
Request is not available in this context exception when runnig mvc on iis7.5

그러나 거기에도 관련 답변이 없습니다.



답변

IIS7 통합 모드 : Application_Start의이 컨텍스트 예외에서 요청을 사용할 수 없음을 참조하십시오 .

“이 컨텍스트에서는 요청을 사용할 수 없습니다”예외는 IIS 7.0에서 ASP.NET 응용 프로그램을 통합 모드로 이동할 때 발생할 수있는 가장 일반적인 오류 중 하나입니다. 이 예외는 응용 프로그램을 시작한 요청의 HttpContext에 액세스하려는 경우 global.asax 파일의 Application_Start 메서드 구현에서 발생합니다.


답변

사용자 정의 로깅 논리가있는 경우 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하지만 정의되지 않을 수 있습니다 RequestResponse정의 (나는 것을 BeginRequest 이벤트에서 볼 수있다). 따라서 사용자 지정 http 모듈에서 요청 / 응답 로깅이 필요한 경우 내 대답이 적합하지 않을 수 있습니다.


답변

이것은 매우 고전적인 경우입니다. http 인스턴스에서 제공하는 데이터를 확인해야한다면 해당 코드를 BeginRequest이벤트 아래로 이동하는 것이 좋습니다.

void Application_BeginRequest(Object source, EventArgs e)

http 헤더, 쿼리 문자열 등을 확인할 수있는 올바른 위치입니다.
Application_Start 라우팅, 필터, 로깅 등과 같이 애플리케이션 전체 런타임에 적용되는 설정을위한 것입니다.

어떤 해결 방법을 적용하지 마십시오 으로부터 코드를 이동하는 방법이없는 경우를 제외하고 정적 .ctor 또는 클래식 모드로 전환 등을 StartBeginRequest. 대부분의 경우에 가능해야합니다.


답변

더 이상 앱을 시작하는 동안 파이프 라인에 요청 컨텍스트가 없기 때문에 다음 실제 요청이 들어올 서버 / 포트를 추측 할 방법이 없다고 상상할 수 없습니다. 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 세부 요구 사항을 기반으로 보다 적절한 솔루션이 존재합니다. OP는 요청과 관련된 데이터 인 log4net을 사용하여 로그에 사용자 정의 데이터를 추가하고 싶다고 말합니다.

각 log4net 호출을 각 로그 호출에서 요청 관련 데이터 검색을 처리하는 사용자 지정 중앙 집중식 로그 호출로 래핑하는 대신 log4net은 로깅 할 사용자 지정 추가 데이터를 설정하기위한 컨텍스트 사전을 제공합니다. 이러한 사전을 사용하면 현재 요청에 대한 요청 로그 데이터를 BeginRequest 이벤트에 배치 한 다음 EndRequest 이벤트에서 해제 할 수 있습니다. 그 사이의 모든 로그인은 이러한 사용자 지정 데이터의 혜택을받습니다.

그리고 요청 컨텍스트에서 발생하지 않는 일은 요청 관련 데이터를 기록하지 않으므로 요청 가용성을 테스트 할 필요가 없습니다. 이 솔루션은 Arman McHitaryan이 그의 답변 에서 제안한 원칙과 일치합니다 .

이 솔루션이 작동하려면 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>

추가 속성, 샘플 구성을 기록하도록 appender를 설정합니다.

<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 이벤트에서 즉시 재사용 할 수 있습니다.


답변

“통합”모드에서 “클래식”모드로 이동하여이 문제를 해결 / 해킹 할 수있었습니다.