asp.net マネージパイプラインモード このコンテキストでは要求を使用できません



このコンテキストでセッション状態は利用できません (8)

私はIIS 7の統合モードを実行しています

リクエストはこのコンテキストでは利用できません

Application_Startから呼び出されたLog4Net関連の関数にアクセスしようとすると、 これは私が行ったコード行です

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

2回目の比較のために例外がスローされます。

ほかに、nullのHttpContext.Current.Requestをチェックする以外に何をチェックできますか?

iis7.5上でrunnig mvcを実行したときに、同様の質問が出されます。

そこには関連する回答もありません。


Answer #1

私は "統合"モードから "クラシック"モードに移行することでこの問題を解決することができました。


Answer #2

アプリケーションの開始時にパイプラインにRequestコンテキストがないので、次の実際のリクエストが来るかもしれないサーバー/ポートを推測する方法はないと思います。 あなたは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);
}

Answer #3

これは非常に古典的なケースです。httpインスタンスによって提供されたデータをチェックする必要が生じた場合、そのコードをBeginRequestイベントの下に移動することを検討してください。

void Application_BeginRequest(Object source, EventArgs e)

これは、httpヘッダー、クエリ文字列などをチェックするのに適した場所ですApplication_Startは、ルーティング、フィルタ、ログなど、アプリケーション全体の実行時間に適用される設定用です。

静的な.ctorのような回避策を適用しないでください 。また、 StartモードからBeginRequestコードを移動する方法がない限り、Classicモードに切り替えることもできません 。 それはあなたの大多数の場合に実行可能でなければなりません。


Answer #4

コメントに記述されたOPの詳細なニーズに基づいて、より適切な解決策が存在します。 OPはlog4netを使ってログにカスタムデータを追加することを希望しています。これはリクエストに関連するデータです。

log4netの各呼び出しを、(各ログ呼び出しで)要求関連データの取得を処理するカスタムの集中ログ呼び出しにラップするのではなく、log4netはカスタム追加データをログに記録するためのコンテキスト辞書を備えています。 これらの辞書を使用すると、BeginRequestイベントで現在のリクエストのリクエストログデータを配置し、EndRequestイベントでそれを却下することができます。 その間にログインすると、これらのカスタムデータが役立ちます。

要求コンテキストでは発生しないものは、要求に関連するデータを記録しようとせず、要求の可用性をテストする必要がありません。 この解決策は、アルマン・マクヒタリヤンのanswerが示唆していた原則に合致していanswer 。

このソリューションが機能するためには、カスタムデータをログするために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>

そして追加のプロパティをログに記録するようにappendersを設定し、設定をサンプルします:

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

Answer #5

従来のモードに切り替えることなく問題を回避することができますが、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イベントですぐに再利用することができます


Answer #6

カスタムロギングロジックを使用している場合は、application_startをログに記録しないようにするか、ロガーで例外が発生するようにしなければなりません(処理したとしても)。

Request可用性をテストするのではなく、 Handler可用性をテストすることができます。 Requestがない場合は依然としてリクエストハンドラがあることが奇妙です。 また、 HandlerテストでRequest is not available in this context例外Request is not available in this context恐ろしいRequest is not available in this context

コードを変更すると、次のようになります。

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

httpモジュールのコンテキストでは、 RequestResponseが定義されているにもかかわらず、 Handlerが定義されていないことがあります(私はBeginRequestイベントでこれを見てきました)。 ですから、カスタムHTTPモジュールでリクエスト/レスポンスのロギングが必要な場合、私の答えは適切ではないかもしれません。


Answer #7

以下を使用できます:

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

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

Answer #8

これは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は、フラグに基づいて意図的に例外をスローします。





iis-7