web-dev-qa-db-ja.com

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

IIS 7統合モードを実行していますが、

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

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

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

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

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


@ iis7.5でrunnig mvcを実行する場合、このコンテキスト例外ではリクエストは利用できません と同様の質問が投稿されています

しかし、関連する答えもありません。

106
Vishal Seth

IIS7統合モード:Application_Startのこのコンテキスト例外では要求を利用できません を参照してください。

「このコンテキストではリクエストは利用できません」という例外は、ASP.NETアプリケーションをIIS 7.0の統合モードに移行する際に発生する可能性のある一般的なエラーの1つです。この例外は、アプリケーションを開始したリクエストのHttpContextにアクセスしようとした場合、global.asaxファイルのApplication_Startメソッドの実装で発生します。

75
Andrew Hare

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

Requestの可用性をテストするのではなく、Handlerの可用性をテストすることができます。Requestがない場合、リクエストハンドラがあるのは不思議です。 Handlerをテストしても、その恐ろしいRequest is not available in this context例外は発生しません。

したがって、コードを次のように変更できます。

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

Httpモジュールのコンテキストでは、HandlerRequestが定義されているにもかかわらず、Responseが定義されていないことに注意してください(BeginRequestイベントで見ました)。したがって、カスタムhttpモジュールで要求/応答のログが必要な場合、私の答えは適切ではないかもしれません。

41
Frédéric

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

void Application_BeginRequest(Object source, EventArgs e)

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

回避策を適用しないでください静的な.ctorやクラシックモードへの切り替えなど、コードをStartからBeginRequestに移動する方法がない場合を除きます。それはあなたの大部分の場合に実行可能でなければなりません。

16

アプリの起動中にパイプラインにリクエストコンテキストがもうないため、次の実際のリクエストがどのサーバー/ポートに到達するかを推測する方法があるとは想像できません。 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);
}
7
Laramie

コメントで説明されているOPの詳細なニーズに基づいて、より適切なソリューションが存在します。 OPは、リクエストに関連するデータであるlog4netを使用して、ログにカスタムデータを追加したいと述べています。

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

また、リクエストコンテキストで発生しないことは、リクエストに関連するデータを記録しようとしないため、リクエストの可用性をテストする必要がなくなります。このソリューションは、Arman McHitaryanが 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>

そして、これらの追加プロパティ、サンプル設定を記録するためにアペンダーを設定します:

<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>
6
Frédéric

クラシックモードに切り替えなくても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イベントですぐに再利用できるようになります。

2
Filip

「統合」モードから「クラシック」モードに移行することで、この問題を回避/ハックすることができました。

1
NiTRiX-Reloaded

Visual Studio 2012で、「デバッグ」オプションを使用して誤ってソリューションを公開すると、この例外が発生しました。 「リリース」オプションを使用すると、発生しませんでした。それが役に立てば幸い。

0
Mimi

これは私にとってはうまくいきました-Application_Startにログインする必要がある場合は、コンテキストを変更する前にログインしてください。次のようなソースなしのログエントリを取得します。

2019-03-12 09:35:43,659情報(null)-アプリケーション開始

通常、Application_StartとSession_Startの両方をログに記録するため、次のメッセージで詳細を確認します

2019-03-12 09:35:45,064 INFO〜/ 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
Jim Ezzell