Yêu cầu không có sẵn trong ngữ cảnh này


113

Tôi đang chạy chế độ Tích hợp IIS 7 và tôi nhận được

Yêu cầu không có sẵn trong ngữ cảnh này

khi tôi cố gắng truy cập nó trong một hàm liên quan đến Log4Net được gọi từ Application_Start. Đây là dòng mã tôi đã

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

và một ngoại lệ đang được đưa ra để so sánh thứ hai.

Tôi có thể kiểm tra gì khác ngoài việc kiểm tra HttpContext.Current.Request cho null ??


Một câu hỏi tương tự được đăng @ Yêu cầu không khả dụng trong ngữ cảnh này ngoại lệ khi runnig mvc trên iis7.5

nhưng cũng không có câu trả lời phù hợp ở đó.


2
Các bạn có khuyên bạn nên thêm khối try-catch làm lựa chọn duy nhất của tôi nếu tôi không thực hiện hai giải pháp khác như được đề xuất trong liên kết từ Andrew Hare không? như thử {if (HttpContext.Current.Request.Headers ["User_info"]! = null) log4net.MDC.Set ("UserInfo", HttpContext.Current.Request.Headers ["User_info"]. ToString ()); } catch () {}
Vishal Seth

Câu trả lời:


79

Vui lòng xem Chế độ tích hợp IIS7: Yêu cầu không khả dụng trong ngoại lệ ngữ cảnh này trong Application_Start :

Ngoại lệ “Yêu cầu không khả dụng trong ngữ cảnh này” là một trong những lỗi phổ biến hơn mà bạn có thể gặp phải khi chuyển ứng dụng ASP.NET sang chế độ Tích hợp trên IIS 7.0. Ngoại lệ này xảy ra khi bạn triển khai phương thức Application_Start trong tệp global.asax nếu bạn cố gắng truy cập vào HttpContext của yêu cầu đã khởi động ứng dụng.


2
Thảo luận thêm về tình huống này tại đây: stackoverflow.com/questions/1790457/…
jball

6
Cảm ơn. Tôi đã thấy liên kết đó trước đây. Nó nói: "Về cơ bản, nếu bạn tình cờ truy cập ngữ cảnh yêu cầu trong Application_Start, bạn có hai lựa chọn: 1) Thay đổi mã ứng dụng của bạn để không sử dụng ngữ cảnh yêu cầu (được khuyến nghị). 2) Di chuyển ứng dụng sang chế độ Cổ điển (KHÔNG được khuyến nghị ). " Họ không có lựa chọn nào khác? Mã ghi nhật ký của tôi ghi nội dung trong DB, ví dụ: Ứng dụng đã bắt đầu, nếu không phải thông qua một yêu cầu ngoài các trường đó nên được đặt thành null thay vì xóa hoàn toàn câu lệnh nhật ký của tôi.
Vishal Seth

Tôi có cùng một loại yêu cầu ghi nhật ký, nếu ngữ cảnh có sẵn, hãy sử dụng nó để điền cơ sở dữ liệu, nếu không để trống các trường. (Trong trường hợp của tôi, không viết một ghi vào một bảng ghi nhật ký, nhưng nó sẽ giúp đỡ nếu có một cách tốt để xác định có hay không có sẵn.)
Zarepheth

2
Không thích nó, nhưng gói kiểm tra trong một try-catch là lựa chọn duy nhất khác hơn là một refactoring lớn của mã của chúng tôi khai thác gỗ (và / hoặc toàn bộ ứng dụng)
Zarepheth

47
Có cách nào để biết bạn có đang ở trong tình huống yêu cầu không có sẵn không? Một số thuộc tính của HttpContext biết về điều này? Tại sao nó lại ném ra một ngoại lệ thay vì chỉ trả về Không có gì, giống như nhiều thuộc tính khác?
Joshua Frank

50

Khi bạn có logic ghi nhật ký tùy chỉnh, thật khó chịu khi bị buộc không đăng nhập application_start hoặc phải để một ngoại lệ xảy ra trong trình ghi nhật ký (ngay cả khi được xử lý).

Có vẻ như thay vì kiểm tra Requesttính khả dụng, bạn có thể kiểm tra Handlertính khả dụng: khi không có Request, sẽ rất lạ nếu vẫn có một trình xử lý yêu cầu. Và thử nghiệm cho Handlerkhông làm tăng Request is not available in this contextngoại lệ đáng sợ đó .

Vì vậy, bạn có thể thay đổi mã của mình thành:

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

Hãy lưu ý, trong ngữ cảnh của mô-đun http, Handlercó thể không được định nghĩa RequestResponseđược xác định (tôi đã thấy điều đó trong sự kiện BeginRequest). Vì vậy, nếu bạn cần đăng nhập yêu cầu / phản hồi trong mô-đun http tùy chỉnh, câu trả lời của tôi có thể không phù hợp.


1
Hơn nữa, những hạn chế đã được nêu ở đây, tôi nhận ra rằng nó thực sự không phải là cách để đáp ứng các nhu cầu cụ thể được OP giải thích trong một bình luận. Xem câu trả lời khác của tôi trên trang này.
Frédéric

1
Điều này đã thực hiện thủ thuật cho tôi, tôi chỉ cần kiểm tra đối tượng Yêu cầu mà không đưa ra ngoại lệ. Ty
OverMars

17

Đây là trường hợp rất cổ điển: Nếu cuối cùng bạn phải kiểm tra bất kỳ dữ liệu nào được cung cấp bởi phiên bản http thì hãy xem xét việc di chuyển mã đó theo BeginRequestsự kiện.

void Application_BeginRequest(Object source, EventArgs e)

Đây là nơi thích hợp để kiểm tra các tiêu đề http, chuỗi truy vấn và v.v. ... Application_Startdành cho các cài đặt áp dụng cho toàn bộ thời gian chạy của ứng dụng, chẳng hạn như định tuyến, bộ lọc, ghi nhật ký, v.v.

Vui lòng không áp dụng bất kỳ cách giải quyết nào như .ctor tĩnh hoặc chuyển sang chế độ Cổ điển trừ khi không có cách nào để chuyển mã từ Startsang BeginRequest. điều đó có thể làm được đối với phần lớn các trường hợp của bạn.


7

Vì không có ngữ cảnh Yêu cầu trong đường dẫn khi khởi động ứng dụng nữa, tôi không thể tưởng tượng có bất kỳ cách nào để đoán máy chủ / cổng nào mà yêu cầu thực tế tiếp theo có thể xuất hiện. Bạn phải làm như vậy trên Begin_Session.

Đây là những gì tôi đang sử dụng khi không ở Chế độ cổ điển. Chi phí không đáng kể.

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

Cảm ơn, điều này đã giúp trang web của tôi hoạt động trở lại sau khi nó đột ngột bị lỗi với triệu chứng này. Thật kỳ lạ, tôi đã không thay đổi từ ASP.NET cổ điển trong nhóm ứng dụng - tôi vẫn gặp lỗi. Thêm một biến thể của mã này (sử dụng Interlocked.Exchange (ref int, int)) đã giải quyết được vấn đề.
John Källén

1
Dòng đầu tiên của câu trả lời này (đây là một bản sao ...) nên được loại bỏ. Đây không phải là bản sao của bài viết được liên kết, câu hỏi khá khác biệt. Anh ta không yêu cầu quyền truy cập vào tên máy chủ khi khởi động ứng dụng. Anh ấy chỉ sẵn sàng để logic ghi nhật ký chung của mình không đưa ra ngoại lệ trong trường hợp đặc biệt application_start.
Frédéric

6

Dựa trên nhu cầu chi tiết của OP được giải thích trong các nhận xét , một giải pháp thích hợp hơn tồn tại. OP nói rằng anh ấy muốn thêm dữ liệu tùy chỉnh vào nhật ký của nó với log4net, dữ liệu liên quan đến các yêu cầu.

Thay vì gói mỗi cuộc gọi log4net thành một lệnh gọi nhật ký tập trung tùy chỉnh xử lý việc truy xuất dữ liệu liên quan đến yêu cầu (trên mỗi cuộc gọi nhật ký), log4net có từ điển ngữ cảnh để thiết lập dữ liệu bổ sung tùy chỉnh để ghi nhật ký. Việc sử dụng các phân số đó cho phép định vị dữ liệu nhật ký yêu cầu của bạn cho yêu cầu hiện tại tại sự kiện BeginRequest, sau đó loại bỏ nó trong sự kiện EndRequest. Bất kỳ đăng nhập nào ở giữa sẽ được hưởng lợi từ những dữ liệu tùy chỉnh đó.

Và những thứ không xảy ra trong ngữ cảnh yêu cầu sẽ không cố gắng ghi dữ liệu liên quan đến yêu cầu, loại bỏ nhu cầu kiểm tra tính khả dụng của yêu cầu. Giải pháp này phù hợp với nguyên tắc mà Arman McHitaryan đã đề xuất trong câu trả lời của mình .

Để giải pháp này hoạt động, bạn cũng sẽ cần một số cấu hình bổ sung trên các trình phụ log4net để chúng ghi dữ liệu tùy chỉnh của bạn.

Giải pháp này có thể dễ dàng thực hiện như một mô-đun nâng cao nhật ký tùy chỉnh. Đây là mã mẫu cho nó:

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

Thêm nó vào trang web của bạn, mẫu conf 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>

Và thiết lập trình phụ để ghi các thuộc tính bổ sung đó, cấu hình mẫu:

<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 để chỉ ra rằng bạn có thể sử dụng HttpContext.Current.Items ["IP"] thay vì HttpContext.Request.UserHostAddress. Trong trường hợp trống Yêu cầu, điều này hoạt động để tìm nạp dữ liệu - đã cứu tôi :) cảm ơn.
Sielu

2

Bạn có thể khắc phục sự cố mà không cần chuyển sang chế độ cổ điển và vẫn sử dụng 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
   }

Vì một số lý do, kiểu tĩnh được tạo với một yêu cầu trong HTTPContext của nó, cho phép bạn lưu trữ và sử dụng lại nó ngay lập tức trong sự kiện Application_Start


Tôi không biết .. Chạy cục bộ có vẻ như nó không "nhìn thấy" cổng khi tôi cố gắng sử dụng: initialRequest.Url.GetLeftPart (UriPartial.Authority); Sẽ phải tìm một cách khác.
justabuzz

Hackish khủng khiếp, nhưng có thể giúp ích trong một số trường hợp tuyệt vọng. (Tôi có một chút cân bằng giữa bỏ phiếu xuống hoặc bỏ phiếu lên, vì vậy tôi chỉ không bỏ phiếu.)
Frédéric

1

Tôi đã có thể giải quyết / khắc phục sự cố này bằng cách chuyển sang chế độ "Cổ điển" từ chế độ "tích hợp".


0

Điều này phù hợp với tôi - nếu bạn phải đăng nhập Application_Start, hãy làm điều đó trước khi bạn sửa đổi ngữ cảnh. Bạn sẽ nhận được một mục nhật ký, chỉ không có nguồn, như:

2019-03-12 09: 35: 43.659 THÔNG TIN (null) - Ứng dụng đã bắt đầu

Tôi thường ghi nhật ký cả Application_Start và Session_Start, vì vậy tôi sẽ thấy chi tiết hơn trong thông báo tiếp theo

2019-03-12 09: 35: 45.064 INFO ~ / Leads / Leads.aspx - Phiên bắt đầu (Địa phương)

        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

Trong visual studio 2012, Khi tôi xuất bản giải pháp do nhầm lẫn với tùy chọn 'gỡ lỗi', tôi đã nhận được ngoại lệ này. Với tùy chọn 'phát hành' nó không bao giờ xảy ra. Hy vọng nó giúp.


-3

Bạn có thể sử dụng sau:

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

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

-4

thực hiện việc này trong 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 + "/");
  // ...
}

hoạt động như một sự quyến rũ. this.Context.Request ở đó ...

this.Request ném ngoại lệ có chủ ý dựa trên một cờ


5
-1: Đọc câu hỏi: đây là lỗi gì (với IIS> = 7 và chế độ Tích hợp)
Richard

Đây là những gì sẽ xảy ra khi hải tặc mất công việc của họ và cố gắng tự trong lập trình :) Không có vi phạm, Man;)
Arman McHitarian
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.