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


113

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

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


2
Andrew Hare의 링크에서 제안한 다른 두 가지 솔루션을 사용하지 않는 경우 유일한 옵션으로 try-catch 블록을 추가하는 것이 좋습니다. try {if (HttpContext.Current.Request.Headers [ "User_info"]! = null) log4net.MDC.Set ( "UserInfo", HttpContext.Current.Request.Headers [ "User_info"]. ToString ()); } catch () {}
Vishal Seth

답변:


79

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

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


2
이 상황에 대한 자세한 내용은 다음을 참조하십시오. stackoverflow.com/questions/1790457/…
jball

6
감사. 나는 그 링크를 전에 보았다. "기본적으로 Application_Start에서 요청 컨텍스트에 액세스하는 경우 두 가지 선택 사항이 있습니다. 1) 요청 컨텍스트를 사용하지 않도록 애플리케이션 코드를 변경합니다 (권장). 2) 애플리케이션을 클래식 모드로 이동합니다 (권장되지 않음). ). " 다른 옵션이 없습니까? 내 로깅 코드는 DB에 내용을 기록합니다. 예를 들어 Application started, 요청을 통하지 않는 경우 해당 필드는 내 로그 문을 완전히 제거하는 대신 null로 설정해야합니다.
Vishal Seth

동일한 종류의 로깅 요구 사항이 있습니다. 컨텍스트를 사용할 수있는 경우 필드를 null로 두지 않으면 데이터베이스를 채우는 데 사용합니다. (제 경우에는 하나의 로깅 테이블에 레코드를 쓰지 마십시오. 그러나 사용 가능 여부를 판단하는 좋은 방법이 있다면 도움이 될 것입니다.)
Zarepheth

2
그것 같이하지 못했지만, 시도 - 캐치에서 수표를 포장하는 것은 우리의 로깅 코드 (및 / 또는 전체 응용 프로그램)의 주요 리팩토링 이외의 유일한 옵션이었다
Zarepheth

47
요청을 사용할 수없는 상황에 있는지 수 있는 방법이 있습니까? 이것에 대해 알고있는 HttpContext의 일부 속성? 다른 많은 속성과 마찬가지로 Nothing을 반환하는 대신 예외가 발생하는 이유는 무엇입니까?
Joshua Frank

50

사용자 정의 로깅 논리가있는 경우 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 모듈에서 요청 / 응답 로깅이 필요한 경우 내 대답이 적합하지 않을 수 있습니다.


1
또한 여기에 이미 언급 된 결점은 OP가 설명하는 특정 요구 사항을 충족하는 방법이 아니라는 것을 깨달았습니다. 이 페이지의 다른 답변을 참조하십시오.
Frédéric

1
이것은 나를 위해 트릭을 수행했으며 예외를 던지지 않고 Request 객체를 확인해야했습니다. Ty
OverMars

17

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

void Application_BeginRequest(Object source, EventArgs e)

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

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


7

더 이상 앱을 시작하는 동안 파이프 라인에 요청 컨텍스트가 없기 때문에 다음 실제 요청이 들어올 서버 / 포트를 추측 할 방법이 없다고 상상할 수 없습니다. 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);
}

감사합니다. 갑자기이 증상이 나타난 후 내 사이트가 다시 실행되었습니다. 이상하게도 앱 풀의 클래식 ASP.NET에서 변경하지 않았습니다. 여전히 오류가 발생했습니다. 이 코드의 변형 (Interlocked.Exchange (ref int, int) 사용)을 추가하면 문제가 해결되었습니다.
John Källén

1
이 답변의 첫 번째 줄 (중복입니다 ...)을 제거해야합니다. 이것은 링크 된 게시물의 중복이 아닙니다. 질문이 상당히 다릅니다. 그는 앱 시작에서 서버 이름에 대한 액세스를 요청하지 않았습니다. 그는 application_start 특수한 경우 예외를 발생시키지 않도록 공통 로깅 논리를 기꺼이 가지고있었습니다.
Frédéric

6

의견에 설명 된 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>

+1. HttpContext.Request.UserHostAddress 대신 HttpContext.Current.Items [ "IP"]를 사용할 수 있음을 지적합니다. 빈 요청의 경우 이것은 데이터 가져 오기에 작동합니다-저를 구했습니다 :) 감사합니다.
Sielu

2

클래식 모드로 전환하지 않고도 문제를 해결하고 여전히 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 이벤트에서 즉시 재사용 할 수 있습니다.


몰라요 .. 로컬로 실행하면 사용하려고 할 때 포트를 "보지"않는 것 같습니다. initialRequest.Url.GetLeftPart (UriPartial.Authority); 다른 방법을 찾아야합니다.
justabuzz

끔찍하게 hackish하지만 일부 절박한 경우에 도움이 될 수 있습니다. (저는 이것에 대해 하향 투표와 상향 투표 사이에 약간 균형이 잡혀 있으므로 투표하지 않습니다.)
Frédéric

1

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


0

이것은 나를 위해 일했습니다-Application_Start에 로그인 해야하는 경우 컨텍스트를 수정하기 전에 수행하십시오. 다음과 같이 소스가없는 로그 항목이 표시됩니다.

2019-03-12 09 : 35 : 43,659 정보 (null)-애플리케이션 시작됨

일반적으로 Application_Start와 Session_Start를 모두 기록하므로 다음 메시지에서 자세한 내용을 볼 수 있습니다.

2019-03-12 09 : 35 : 45,064 정보 ~ / Leads / Leads.aspx-세션 시작됨 (로컬)

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


0

Visual Studio 2012에서 '디버그'옵션으로 실수로 솔루션을 게시했을 때이 예외가 발생했습니다. 'release'옵션을 사용하면 결코 발생하지 않았습니다. 도움이 되었기를 바랍니다.


-3

다음을 사용할 수 있습니다.

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

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

-4

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

매력처럼 작동합니다. this.Context.Request가 있습니다 ...

this.Request는 플래그를 기반으로 의도적으로 예외를 throw합니다.


5
-1 : 질문 읽기 : 이것이 실패한 것입니다 (IIS> = 7 및 통합 모드 사용)
Richard

이것은 해적들이 직장을
잃고
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.