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


113

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

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

私はから呼び出されlog4netの関連機能でそれにアクセスしようとしますApplication_Start。これは私が書いたコード行です

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

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

HttpContext.Current.Requestのnullを確認する以外に、他に何を確認できますか?


同様の質問が投稿されます@ Iis7.5でMVCを実行すると、このコンテキスト例外ではリクエストを使用できません

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


2
Andrew Hareからのリンクで提案されている他の2つのソリューションを採用しない場合は、try-catchブロックを唯一のオプションとして追加することをお勧めしますか?のように試してください{if(HttpContext.Current.Request.Headers ["User_info"]!= null)log4net.MDC.Set( "UserInfo"、HttpContext.Current.Request.Headers ["User_info"]。ToString()); } catch(){}
Vishal Seth 2010

回答:


79

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

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


2
:ここではこのような状況のより詳細な議論stackoverflow.com/questions/1790457/...
jball

6
ありがとう。以前にそのリンクを見たことがあります。「基本的に、たまたまApplication_Startで要求コンテキストにアクセスしている場合、2つの選択肢があります。1)要求コンテキストを使用しないようにアプリケーションコードを変更します(推奨)。2)アプリケーションをクラシックモードに移動します(非推奨) )」他に選択肢はありませんか?私のロギングコードはDBに何かを書き込みます。たとえば、アプリケーションが開始された場合、リクエストを介さない場合、ログステートメントを完全に削除するのではなく、これらのフィールドをnullに設定する必要があります。
Vishal Seth

同じ種類のロギング要件があります。コンテキストが使用可能な場合は、それを使用してデータベースにデータを入力します。そうでない場合は、フィールドをnullのままにします。(私の場合、1つのロギングテーブルにレコードを書き込まないでください。ただし、それが利用可能かどうかを判断する良い方法がある場合に役立ちます。)
Zarepheth

2
気に入らなかったが、チェックをtry-catchでラップすることが、ロギングコード(および/またはアプリ全体)の大規模なリファクタリング以外の唯一の選択肢であった
Zarepheth

47
何か方法があり告げるあなたが要求は使用できません状況にいる場合は?これを知っているHttpContextのいくつかのプロパティ?他の多くのプロパティのように、何も返さずに例外をスローするのはなぜですか?
ジョシュアフランク

50

カスタムロギングロジックがある場合、application_startをログに記録しないか、(処理されたとしても)ロガーで例外を発生させる必要があるので、どちらかといえば面倒です。

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

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

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

、A HTTPモジュールとの関連で、注意Handlerも定義されないことRequestResponse定義されている(私は、BeginRequestイベントのイベントで見ています)。したがって、カスタムhttpモジュールでリクエスト/レスポンスのロギングが必要な場合、私の答えは適切ではない可能性があります。


1
さらに、ここですでに述べた欠点は、OPがコメントで説明する特定のニーズに取り組む方法ではないことに気付きました。このページで他の回答を参照してください。
フレデリック2014年

1
これは私にとってトリックでした、私は例外をスローすることなくRequestオブジェクトをチェックする必要があるだけでした。Ty
OverMars

17

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

void Application_BeginRequest(Object source, EventArgs e)

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

、いずれかの回避策は適用しないでくださいからコードを移動する方法はありませんしない限り、このような静的.ctorやクラシックモードへの切り替えなどをStartしますBeginRequest。それはあなたのケースの大部分に対して実行可能であるべきです。


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)を使用)、問題が解決しました。
JohnKällén12年

1
この回答の最初の行(これは重複しています...)を削除する必要があります。これはリンクされた投稿の複製ではありません。質問はかなり異なります。アプリの起動時にサーバー名にアクセスするように求めていませんでした。彼は、application_startの特殊なケースで例外をスローしないように、共通のロギングロジックを用意するつもりでした。
フレデリック14

6

コメントで説明されているOPの詳細なニーズに基づいて、より適切なソリューションが存在します。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();
        }
    }
}

それをサイトに追加します。IIS7+ 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>

+1は、HttpContext.Request.UserHostAddressの代わりにHttpContext.Current.Items ["IP"]を使用できることを示します。空のリクエストの場合、これはデータを取得するために機能します-これは私を救った:)ありがとうございます。
Sielu 2016年

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

ひどくハックですが、いくつかの絶望的なケースでは役立つかもしれません。(私はこれに反対票を投じるか、賛成票を投じるので少しバランスが取れているので、投票しません。)
フレデリック

1

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


0

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

2019-03-12 09:35:43,659 INFO(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

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


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


5
-1:質問を読んでください:これは失敗しています(IIS> = 7および統合モードの場合)
Richard

これは、海賊が自分の仕事を
失い
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.