Permintaan tidak tersedia dalam konteks ini

113

Saya menjalankan mode Terpadu IIS 7 dan saya mendapatkan

Permintaan tidak tersedia dalam konteks ini

ketika saya mencoba mengaksesnya di fungsi terkait Log4Net yang dipanggil dari Application_Start. Ini adalah baris kode yang saya miliki

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

dan pengecualian dilemparkan untuk perbandingan kedua.

Apa lagi yang bisa saya periksa selain memeriksa HttpContext.Current.Request untuk null ??


Pertanyaan serupa diposting @ Permintaan tidak tersedia dalam pengecualian konteks ini ketika runnig mvc di iis7.5

tapi tidak ada jawaban yang relevan di sana.

Vishal Seth
sumber
2
Apakah kalian akan merekomendasikan untuk menambahkan blok coba-tangkap sebagai satu-satunya pilihan saya jika saya tidak mengambil dua solusi lainnya seperti yang disarankan dalam tautan dari Andrew Hare? seperti coba {if (HttpContext.Current.Request.Headers ["User_info"]! = null) log4net.MDC.Set ("UserInfo", HttpContext.Current.Request.Headers ["User_info"]. ToString ()); } catch () {}
Vishal Seth

Jawaban:

79

Silakan lihat mode Terpadu IIS7: Permintaan tidak tersedia dalam pengecualian konteks ini di Application_Start :

Pengecualian "Permintaan tidak tersedia dalam konteks ini" adalah salah satu kesalahan yang lebih umum yang mungkin Anda terima saat memindahkan aplikasi ASP.NET ke mode Terpadu di IIS 7.0. Pengecualian ini terjadi dalam implementasi metode Application_Start di file global.asax jika Anda mencoba mengakses HttpContext dari permintaan yang memulai aplikasi.

Andrew Hare
sumber
2
Diskusi lebih lanjut tentang situasi ini di sini: stackoverflow.com/questions/1790457/…
jball
6
Terima kasih. Saya telah melihat tautan itu sebelumnya. Bunyinya: "Pada dasarnya, jika Anda kebetulan mengakses konteks permintaan di Application_Start, Anda memiliki dua pilihan: 1) Ubah kode aplikasi Anda untuk tidak menggunakan konteks permintaan (disarankan). 2) Pindahkan aplikasi ke mode Klasik (TIDAK disarankan ). " Apakah mereka tidak punya pilihan lain? Kode logging saya menulis hal-hal di DB misalnya Aplikasi dimulai, jika tidak melalui permintaan daripada bidang tersebut harus disetel ke nol daripada sepenuhnya menghapus pernyataan log saya.
Vishal Seth
Saya memiliki persyaratan pencatatan yang sama, jika konteks tersedia, gunakan untuk mengisi database, jika tidak biarkan kolom kosong. (Dalam kasus saya, jangan menulis catatan ke satu tabel logging, tetapi akan membantu jika ada cara yang baik untuk menentukan apakah tersedia atau tidak.)
Zarepheth
2
Tidak menyukainya, tetapi membungkus cek dalam uji coba adalah satu-satunya pilihan selain
pemfaktoran ulang
47
Adakah cara untuk mengetahui apakah Anda berada dalam situasi di mana permintaan tidak akan tersedia? Beberapa properti HttpContext yang mengetahui tentang ini? Mengapa itu membuat pengecualian dan bukan hanya mengembalikan Nothing, seperti banyak properti lainnya?
Joshua Frank
50

Ketika Anda memiliki logika logging kustom, agak menjengkelkan untuk dipaksa baik untuk tidak mencatat application_start atau harus membiarkan pengecualian terjadi di logger (bahkan jika ditangani).

Tampaknya daripada menguji Requestketersediaan, Anda dapat menguji Handlerketersediaan: jika tidak ada Request, akan aneh untuk tetap memiliki penangan permintaan. Dan pengujian untuk Handlertidak memunculkan Request is not available in this contextpengecualian yang ditakuti itu .

Jadi, Anda dapat mengubah kode Anda menjadi:

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

Hati-hati, dalam konteks modul http, Handlermungkin tidak didefinisikan Requestdan Responsedidefinisikan (saya telah melihat itu di acara BeginRequest). Jadi jika Anda membutuhkan permintaan / tanggapan logging di modul http kustom, jawaban saya mungkin tidak cocok.

Frédéric
sumber
1
Selain itu kekurangannya sudah dinyatakan di sini, saya menyadari itu benar-benar bukan cara untuk memenuhi kebutuhan spesifik yang dijelaskan oleh OP dalam komentar. Lihat jawaban saya yang lain di halaman ini.
Frédéric
1
Ini melakukan trik untuk saya, saya hanya perlu memeriksa objek Request tanpa mengeluarkan pengecualian. Ty
OverMars
17

Ini adalah kasus yang sangat klasik: Jika Anda akhirnya harus memeriksa data apa pun yang disediakan oleh contoh http, maka pertimbangkan untuk memindahkan kode itu di bawah BeginRequestacara tersebut.

void Application_BeginRequest(Object source, EventArgs e)

Ini adalah tempat yang tepat untuk memeriksa header http, string kueri, dan lain-lain ... Application_Startuntuk pengaturan yang berlaku untuk aplikasi sepanjang waktu berjalan, seperti perutean, filter, logging, dan sebagainya.

Mohon, jangan terapkan solusi apa pun seperti .ctor statis atau beralih ke mode Klasik kecuali tidak ada cara untuk memindahkan kode dari Startke BeginRequest. yang seharusnya bisa dilakukan untuk sebagian besar kasus Anda.

Arman McHitarian
sumber
7

Karena tidak ada lagi konteks Permintaan dalam pipeline selama aplikasi dimulai, saya tidak dapat membayangkan ada cara untuk menebak server / port apa yang mungkin digunakan oleh permintaan aktual berikutnya. Anda harus melakukannya di Begin_Session.

Inilah yang saya gunakan saat tidak dalam Mode Klasik. Overhead dapat diabaikan.

/// <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);
}
Laramie
sumber
Terima kasih, ini membuat situs saya aktif dan berjalan kembali setelah tiba-tiba mengalami gejala ini. Anehnya, saya belum mengubah dari ASP.NET klasik di pool aplikasi - Saya masih mendapat kesalahan. Menambahkan varian dari kode ini (menggunakan Interlocked.Exchange (ref int, int)) memecahkan masalah.
John Källén
1
Baris pertama dari jawaban ini (ini adalah duplikat ...) harus dihilangkan. Ini bukan duplikat dari postingan tertaut, pertanyaannya sangat berbeda. Dia tidak meminta untuk mengakses nama server di awal aplikasi. Dia hanya ingin menggunakan logika logging umum untuk tidak menampilkan pengecualian dalam kasus khusus application_start.
Frédéric
6

Berdasarkan kebutuhan rinci OP yang dijelaskan dalam komentar , ada solusi yang lebih tepat. OP menyatakan dia ingin menambahkan data khusus di lognya dengan log4net, data yang terkait dengan permintaan.

Daripada menggabungkan setiap panggilan log4net menjadi panggilan log terpusat kustom yang menangani pengambilan data terkait permintaan (pada setiap panggilan log), log4net menampilkan kamus konteks untuk menyiapkan data tambahan kustom ke log. Menggunakan kamus tersebut memungkinkan untuk memposisikan data log permintaan Anda untuk permintaan saat ini di acara BeginRequest, lalu menutupnya di acara EndRequest. Setiap log in di antaranya akan mendapatkan keuntungan dari data kustom tersebut.

Dan hal-hal yang tidak terjadi dalam konteks permintaan tidak akan mencoba mencatat data terkait permintaan, menghilangkan kebutuhan untuk menguji ketersediaan permintaan. Solusi ini sesuai dengan prinsip yang disarankan Arman McHitaryan dalam jawabannya .

Agar solusi ini berfungsi, Anda juga memerlukan beberapa konfigurasi tambahan pada appenders log4net Anda agar mereka dapat mencatat data khusus Anda.

Solusi ini dapat dengan mudah diimplementasikan sebagai modul peningkatan log kustom. Berikut adalah contoh kode untuk itu:

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

Tambahkan ke situs Anda, contoh konfigurasi IIS 7+:

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

Dan siapkan appenders untuk membuat log properti tambahan tersebut, contoh konfigurasi:

<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>
Frédéric
sumber
1 untuk menunjukkan bahwa Anda dapat menggunakan HttpContext.Current.Items ["IP"] daripada HttpContext.Request.UserHostAddress. Dalam kasus Request kosong ini berfungsi untuk mengambil data - yang menyelamatkan saya :) terima kasih.
Sielu
2

Anda dapat mengatasi masalah ini tanpa beralih ke mode klasik dan tetap menggunakan 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
   }

Untuk beberapa alasan, tipe statis dibuat dengan permintaan dalam HTTPContext-nya, memungkinkan Anda untuk menyimpan dan menggunakannya kembali segera di acara Application_Start

Filip
sumber
Entahlah .. Berjalan secara lokal tampaknya tidak "melihat" port ketika saya mencoba menggunakan: initialRequest.Url.GetLeftPart (UriP Partial.Authority); Akan harus mencari cara yang berbeda.
justabuzz
Sangat meretas, tetapi dapat membantu dalam beberapa kasus putus asa. (Saya agak seimbang antara down-voting atau up-voting ini, jadi saya hanya tidak memberikan suara.)
Frédéric
1

Saya dapat mengatasi / meretas masalah ini dengan berpindah ke mode "Klasik" dari mode "terintegrasi".

nithinmohantk.dll
sumber
0

Ini berhasil untuk saya - jika Anda harus masuk ke Application_Start, lakukan sebelum Anda mengubah konteks. Anda akan mendapatkan entri log, hanya tanpa sumber, seperti:

2019-03-12 09: 35: 43.659 INFO (null) - Aplikasi Dimulai

Saya biasanya mencatat baik Application_Start dan Session_Start, jadi saya melihat lebih detail di pesan berikutnya

2019-03-12 09: 35: 45.064 INFO ~ / Leads / Leads.aspx - Sesi Dimulai (Lokal)

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

Jim Ezzell
sumber
0

Di studio visual 2012, Ketika saya menerbitkan solusi secara keliru dengan opsi 'debug', saya mendapat pengecualian ini. Dengan opsi 'rilis' itu tidak pernah terjadi. Semoga membantu.

Mimi
sumber
-3

Anda dapat menggunakan berikut ini:

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

    private void StartMySystem(object state)
    {
        Log(HttpContext.Current.Request.ToString());
    }
Maxim Zabuti
sumber
-4

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

bekerja seperti pesona. this.Context.Request ada ...

this.Request memunculkan pengecualian secara sengaja berdasarkan sebuah flag

Gergely Herendi
sumber
5
-1: Baca pertanyaan: inilah yang gagal (dengan IIS> = 7 dan mode Terpadu)
Richard
Inilah yang terjadi ketika bajak laut kehilangan pekerjaan mereka dan mencoba sendiri dalam pemrograman :) Jangan tersinggung, Bung;)
Arman McHitarian