Application_Error không kích hoạt khi customerrors = trên On


124

Tôi có mã trong sự kiện global.asaxcủa tệp sẽ Application_Errorthực thi khi xảy ra lỗi và gửi email chi tiết về lỗi cho chính tôi.

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();

    if (error.Message != "Not Found")
    {
        // Send email here...
    }

}

Điều này hoạt động tốt khi tôi chạy nó trong Visual Studio, tuy nhiên khi tôi xuất bản lên máy chủ trực tiếp của chúng tôi thì Application_Errorsự kiện không kích hoạt.

Sau một số thử nghiệm, tôi có thể thực hiện Application_Errorbắn khi tôi đặt customErrors="Off", tuy nhiên cài đặt lại để customErrors="On"ngăn sự kiện bắn lại.

Bất cứ ai có thể đề nghị tại sao Application_Errorsẽ không bắn khi customErrorsđược kích hoạt trong web.config?


Tôi đang có cùng một vấn đề. Tôi cũng tìm thấy câu hỏi SO này: stackoverflow.com/questions/3713939/ trên đó gợi ý đưa máy chủ IIS7 vào chế độ cổ điển. Thật không may, đó không phải là một lựa chọn cho chúng tôi. Bất cứ ai có giải pháp tốt hơn?
Jesse Webb

Dưới đây là một câu hỏi có liên quan và đó là câu trả lời (không ai trong số đó được chấp nhận) đề nghị không sử dụng Application_Error () ở tất cả ... stackoverflow.com/questions/1194578/...
Jesse Webb

@Gweebz Tôi đã đăng một câu trả lời về cách tôi khắc phục điều này, nhưng tôi vẫn không tìm thấy bất kỳ tài liệu vững chắc nào về lý do tại sao tôi lại có hành vi này.
WDuffy

Tôi đã thêm một câu trả lời giải thích tại sao Application_Error()phương thức không được gọi. Tôi cũng đã giải thích giải pháp cuối cùng của tôi.
Jesse Webb

Thực sự đề nghị bài viết này để có được các lỗi tùy chỉnh làm việc.
ThomasArdal

Câu trả lời:


133

CẬP NHẬT
Vì câu trả lời này không cung cấp giải pháp, tôi sẽ không chỉnh sửa nó, nhưng tôi đã tìm ra cách giải quyết vấn đề này gọn gàng hơn nhiều. Xem câu trả lời khác của tôi để biết chi tiết ...

Câu trả lời gốc:
Tôi đã tìm ra lý do tại sao Application_Error()phương thức này không được gọi ...

Toàn cầu.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new HandleErrorAttribute()); // this line is the culprit
    }
...
}

Theo mặc định (khi một dự án mới được tạo), một ứng dụng MVC có một số logic trong Global.asax.cs tệp. Logic này được sử dụng để ánh xạ các tuyến đường và đăng ký bộ lọc. Theo mặc định, nó chỉ đăng ký một bộ lọc: HandleErrorAttributebộ lọc. Khi customErrors được bật (hoặc thông qua các yêu cầu từ xa khi được đặt thành RemoteOnly), HandleErrorAttribution nói với MVC để tìm chế độ xem Lỗi và nó không bao giờ gọi Application_Error()phương thức. Tôi không thể tìm thấy tài liệu về điều này nhưng nó được giải thích trong câu trả lời này trên lập trình viên.stackexchange.com .

Để có được phương thức ApplicationError () được gọi cho mọi ngoại lệ chưa được xử lý, chỉ cần xóa dòng đăng ký bộ lọc HandleErrorAttribution.

Bây giờ vấn đề là: Làm cách nào để định cấu hình customErrors để có được thứ bạn muốn ...

Phần customErrors mặc định là redirectMode="ResponseRedirect" . Bạn cũng có thể chỉ định thuộc tính defaultRedirect là một tuyến MVC. Tôi đã tạo một ErrorContoder rất đơn giản và đã thay đổi web.config của mình thành như thế này ...

web.config

<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Error">
  <error statusCode="404" redirect="~/Error/PageNotFound" />
</customErrors>

Vấn đề với giải pháp này là nó thực hiện chuyển hướng 302 đến các URL lỗi của bạn và sau đó các trang đó phản hồi với mã trạng thái 200. Điều này dẫn đến việc Google lập chỉ mục các trang lỗi. Nó cũng không phù hợp với thông số HTTP. Những gì tôi muốn làm là không chuyển hướng và ghi đè phản hồi ban đầu với các chế độ xem lỗi tùy chỉnh của tôi.

Tôi đã cố gắng thay đổi redirectMode="ResponseRewrite". Thật không may, tùy chọn này không hỗ trợ các tuyến MVC , chỉ có các trang HTML tĩnh hoặc ASPX. Tôi đã cố gắng sử dụng một trang HTML tĩnh lúc đầu nhưng mã phản hồi vẫn là 200 nhưng, ít nhất nó không chuyển hướng. Sau đó tôi có một ý tưởng từ câu trả lời này ...

Tôi quyết định từ bỏ MVC để xử lý lỗi. Tôi đã tạo ra một Error.aspxvà một PageNotFound.aspx. Những trang này rất đơn giản nhưng chúng có một mảnh ma thuật ...

<script type="text/C#" runat="server">
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        Response.StatusCode = (int) System.Net.HttpStatusCode.InternalServerError;
    }
</script>

Khối này cho biết trang sẽ được phục vụ với mã trạng thái chính xác. HttpStatusCode.NotFoundThay vào đó , trên trang PageNotFound.aspx, tôi đã sử dụng thay thế. Tôi đã thay đổi web.config của mình thành như thế này ...

<customErrors mode="RemoteOnly" redirectMode="ResponseRewrite" defaultRedirect="~/Error.aspx">
  <error statusCode="404" redirect="~/PageNotFound.aspx" />
</customErrors>

Tất cả đều hoạt động hoàn hảo!

Tóm lược:

  • Xóa dòng: filters.Add(new HandleErrorAttribute());
  • Sử dụng Application_Error() phương pháp để ghi lại các ngoại lệ
  • Sử dụng CustomErrors với FeedbackRewrite, chỉ vào các trang ASPX
  • Làm cho các trang ASPX chịu trách nhiệm về mã trạng thái phản hồi của riêng họ

Có một vài nhược điểm tôi đã nhận thấy với giải pháp này.

  • Các trang ASPX không thể chia sẻ bất kỳ đánh dấu nào với các mẫu Dao cạo, tôi đã phải viết lại đánh dấu tiêu đề và chân trang tiêu chuẩn của trang web của chúng tôi để có giao diện nhất quán.
  • Các trang * .aspx có thể được truy cập trực tiếp bằng cách nhấn URL của họ

Có những cách giải quyết cho những vấn đề này nhưng tôi không đủ quan tâm đến họ để làm thêm.

Tôi hy vọng điều này sẽ giúp tất cả mọi người!


2
+1! Giải pháp thực sự tốt nhưng, hậu quả của việc trộn MVC với các trang aspx là gì?
Diego

2
Chúng tôi đã có điều này trong sản xuất trong một vài tháng nay và tôi đã không tìm thấy và tác động tiêu cực. 'Gotcha' duy nhất là chúng tôi phải thay đổi CI và triển khai để thực hiện tác vụ AspCompile MSBUILD vì MVC không cần nhưng khi chúng tôi thêm các tệp .as , họ yêu cầu. Có thể có những vấn đề khác nhưng chúng chưa xuất hiện ...
Jesse Webb

Tôi đã cố gắng sử dụng phương pháp này trong MVC4 và dường như nó chỉ hoạt động với tôi khi tôi có <customErrors mode="Off" />. Có filters.Add(new HandleErrorAttribute());loại bỏ hoặc không có hiệu lực.
Grzegorz Sławecki

trên trang aspx, bạn có thể thêm một cuộc gọi ajax để gọi chế độ xem bạn muốn hiển thị. Lưu phải sao chép mã
Mike

71

Tôi đã giải quyết điều này bằng cách tạo ExceptionFilter và ghi lại lỗi ở đó thay vì Application_Error. Tất cả những gì bạn cần làm là thêm một cuộc gọi vào trong RegisterGlobalFilters

log4netExceptionFilter.cs

using System
using System.Web.Mvc;

public class log4netExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        Exception ex = context.Exception;
        if (!(ex is HttpException)) //ignore "file not found"
        {
            //Log error here
        }
    }
}

Toàn cầu.asax.cs

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new log4netExceptionFilter()); //must be before HandleErrorAttribute
    filters.Add(new HandleErrorAttribute());
}

6
+1 Nếu câu trả lời này hoạt động, có vẻ như tôi chắc chắn là cách thanh lịch nhất và có lẽ được dự định (theo khung MVC) để xử lý lỗi.
Matt Hamsmith

2
Điều này làm việc hoàn hảo cho tôi và gọn gàng hơn nhiều so với câu trả lời được chấp nhận ở đây. Đẹp!
Alex Warren

3
Điều này sẽ làm việc để giải quyết vấn đề ngoại lệ đăng nhập. Làm thế nào bạn kết hợp điều này với hiển thị các trang lỗi tùy chỉnh?
Jesse Webb

3
Tôi đã thử điều này và nó hoạt động tốt, ngoại trừ trong trường hợp 404s. Đối với 404, nó sẽ không hiển thị chế độ xem lỗi.cshtml, nó sẽ chỉ cung cấp cho tôi một YSoD. Nếu không cần 404 tùy chỉnh, giải pháp này chắc chắn sạch hơn!
Jesse Webb

1
Tôi thích nó. Hoạt động với 'CustomErrors = On'
Illidan

36

Tôi đã tìm thấy một bài viết mô tả một cách dễ dàng hơn để tạo các trang lỗi tùy chỉnh trong ứng dụng web MVC3 không ngăn chặn khả năng ghi lại các ngoại lệ.

Giải pháp là sử dụng <httpErrors>phần tử của <system.webServer>phần.

Tôi đã cấu hình Web.config của mình như vậy ...

<httpErrors errorMode="DetailedLocalOnly" existingResponse="Replace">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
  <error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>

Tôi cũng cấu hình customErrorsđể cómode="Off" (theo đề xuất của bài viết).

Điều đó làm cho các phản hồi bị ghi đè bởi các hành động của ErrorContoder. Đây là bộ điều khiển:

public class ErrorController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult NotFound()
    {
        return View();
    }
}

Các khung nhìn rất thẳng về phía trước, tôi chỉ sử dụng cú pháp Dao cạo tiêu chuẩn để tạo các trang.

Chỉ vậy thôi là đủ để bạn sử dụng các trang lỗi tùy chỉnh với MVC.

Tôi cũng cần ghi nhật ký Ngoại lệ vì vậy tôi đã đánh cắp giải pháp của Mark bằng cách sử dụng ExceptionFilter tùy chỉnh ...

public class ExceptionPublisherExceptionFilter : IExceptionFilter
{
    public void OnException(ExceptionContext exceptionContext)
    {
        var exception = exceptionContext.Exception;
        var request = exceptionContext.HttpContext.Request;
        // log stuff
    }
}

Điều cuối cùng bạn cần là đăng ký Bộ lọc ngoại lệ trong tệp Global.asax.cs của bạn :

public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
    filters.Add(new ExceptionPublisherExceptionFilter());
    filters.Add(new HandleErrorAttribute());
}

Điều này cảm thấy giống như một giải pháp sạch hơn nhiều so với câu trả lời trước đây của tôi và hoạt động tốt như tôi có thể nói. Tôi đặc biệt thích nó vì tôi không cảm thấy như mình đang chiến đấu chống lại khung công tác MVC; giải pháp này thực sự tận dụng nó!


6
Điều đáng nói là tôi nghĩ giải pháp này chỉ hoạt động trong IIS 7 và mới hơn; phần tử httpErrors chỉ được thêm gần đây.
Jesse Webb

Câu trả lời này không hiệu quả với tôi, nó có tôi, nó chơi cho tôi một màu xanh khủng khiếp: HTTP Error 500.0 - Internal Server Error The page cannot be displayed because an internal server error has occurred. Màn hình lỗi, không phải /Error/Indextrang tôi yêu cầu
Serj Sagan

@SerjSagan Tôi vừa thử điều này trong một dự án mới để thử nghiệm các bước và nó hoạt động tốt. Bạn đang sử dụng IIS, IIS Express hoặc VS Dev Server? Điều này sẽ không hoạt động trong VS Dev Server. Khi tôi cài đặt dự án của mình để sử dụng VS Dev Server, không phải IIS, tôi nhận thấy lỗi màn hình màu vàng thay vì các trang lỗi tùy chỉnh.
Jesse Webb

1
@SerjSagan Nhưng có vẻ như bạn đang gặp lỗi màn hình IIS Blue, trái ngược với các lỗi màn hình vàng cổ điển. Điều này khiến tôi cho rằng bạn đang sử dụng một số dạng IIS. Nếu vậy, hãy đọc qua bài viết mà tôi đã liên kết trong câu đầu tiên, cụ thể là phần cuối cùng có tiêu đề: "Vài ghi chú quan trọng". Nó nói:To able to overwrite global settings in global IIS settings (file: C:\Windows\System32\inetsrv\config \applicationHost.config) should be: <section name="httpErrors" overrideModeDefault="Allow" />
Jesse Webb

3
@SerjSagan Thay đổi errorMode thành Chi tiếtLocalOnly hoặc Tùy chỉnh sẽ hiển thị trang của bạn.
Daniel P

8

Trong trường hợp sử dụng ASP.NET MVC5

public class ExceptionPublisherExceptionFilter : IExceptionFilter
    {
        private static Logger _logger = LogManager.GetCurrentClassLogger();
        public void OnException(ExceptionContext exceptionContext)
        {
            var exception = exceptionContext.Exception;
            var request = exceptionContext.HttpContext.Request;
            // HttpException httpException = exception as HttpException;
            // Log this exception with your logger
            _logger.Error(exception.Message);
        }
    }

Bạn có thể tìm thấy nó trong FilterConfig.cscác App_Startthư mục.


1

Tôi thích câu trả lời của Mark với ExceptionFilter, nhưng một tùy chọn khác, nếu bạn có tất cả các bộ điều khiển của mình xuất phát từ cùng một bộ điều khiển cơ bản, chỉ đơn giản là ghi đè OnException trong bộ điều khiển cơ sở của bạn. Bạn có thể đăng nhập và gửi email ở đó. Điều này có lợi thế là có thể sử dụng bất kỳ phụ thuộc nào mà bạn đã đưa vào bộ điều khiển cơ sở với bộ chứa IoC của mình.

Bạn vẫn có thể sử dụng IoC của mình với IExceptionFilter, nhưng sẽ khó hơn một chút để định cấu hình các liên kết của bạn.


0

Theo tôi biết, bạn đang chuyển quyền kiểm soát tới Trang được chỉ định trong tham số url và thông báo sự kiện của bạn sẽ ở đây, thay vì Application_Error

<customErrors defaultRedirect="myErrorPage.aspx"
              mode="On">
</customErrors>

Rất nhiều thông tin có thể được tìm thấy ở đây: http://support.microsoft.com/kb/306355


Cảm ơn Chris, tôi đã đọc tài liệu này và nó gợi ý rằng Application_Error sẽ được gọi khi xảy ra lỗi chưa xử lý (mà tôi mong đợi) và đó không phải là phần Server.learError () không được gọi là phần xử lý tùy chỉnh web.config sẽ là điểm xử lý cuối cùng. Tuy nhiên, việc bật customErrors là thứ đang ngăn Application_Error bắn.
WDuffy

Tài liệu bạn liên kết đến đang nói về ứng dụng ASP.NET và có vẻ như các ứng dụng web MVC3 đang hoạt động khác đi.
Jesse Webb

0

Để giải quyết vấn đề này, cuối cùng tôi đã để các tùy biến bị vô hiệu hóa và xử lý tất cả các lỗi từ sự kiện Application_Error trong global.asax. Nó hơi khó với MVC vì tôi không muốn trả về chuyển hướng 301, tôi muốn trả về mã lỗi phù hợp. Thông tin chi tiết có thể được xem trên blog của tôi tại http://www.wduffy.co.uk/blog/USE-application_error-in-asp-net-mvcs-global-asax-to-handle-errors/ nhưng mã cuối cùng là được liệt kê dưới đây...

void Application_Error(object sender, EventArgs e)
{
    var error = Server.GetLastError();
    var code = (error is HttpException) ? (error as HttpException).GetHttpCode() : 500;

    if (code != 404)
    {
            // Generate email with error details and send to administrator
    }

    Response.Clear();
    Server.ClearError();

    string path = Request.Path;
    Context.RewritePath(string.Format("~/Errors/Http{0}", code), false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(Context);
    Context.RewritePath(path, false);
}

Và đây là bộ điều khiển

public class ErrorsController : Controller
{

    [HttpGet]
    public ActionResult Http404(string source)
    {
            Response.StatusCode = 404;
            return View();
    }

    [HttpGet]
    public ActionResult Http500(string source)
    {
            Response.StatusCode = 500;
            return View();
    }

}

Tôi đã thử giải pháp của bạn nhưng nó không hiệu quả với tôi. Với các dòng Response.StatusCode = ###;, nó đã hiển thị các trang lỗi MVC tích hợp tại C:\inetpub\custerr\en-US. Tôi cũng không thích ý tưởng gọi thủ công hoặc Bộ điều khiển của httpHandlers từ phương thức Application_Error () của mình. Tôi rất vui vì bạn đã tìm ra giải pháp cho vấn đề của mình, tôi biết loại đau đầu này đã mang lại cho tôi.
Jesse Webb

0

Mục blog này đã giúp tôi:

http://asp-net.vexedlogic.com/2011/04/23/asp-net-maximum-request-length-exceeded/

Nếu bạn đang sử dụng IIS 7.0 trở lên, bạn có thể thay đổi tệp Web.config để xử lý các yêu cầu quá lớn. Có một số cảnh báo, nhưng đây là một ví dụ:

<system.webServer>
  <security>
    <requestFiltering>
      <requestLimits maxAllowedContentLength="1048576" />
    </requestFiltering>
  </security>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" subStatusCode="13" />
    <error statusCode="404" subStatusCode="13" prefixLanguageFilePath="" path="/UploadTooLarge.htm" responseMode="Redirect" />
  </httpErrors>
</system.webServer>

Có các chi tiết bổ sung về các thành phần tệp cấu hình ở đây:

http://www.iis.net/ConfigReference/system.webServer/security/requestFiltering/requestLimits

Mã trạng thái 404.13 được định nghĩa là "Độ dài nội dung quá lớn". Một điều quan trọng cần lưu ý là maxAllowedContentLengthđược chỉ định bằng byte. Điều này khác với maxRequestLengthcài đặt bạn tìm thấy trong <system.web>phần, được chỉ định bằng kilobyte.

<system.web>
  <httpRuntime maxRequestLength="10240" />
</system.web>

Cũng lưu ý rằng paththuộc tính phải là một đường dẫn tuyệt đối khi responseModeRedirect, vì vậy hãy thêm tên thư mục ảo, nếu có liên quan. Các câu trả lời thông tin của Jesse Webb cho thấy cách thực hiện điều này responseMode="ExecuteURL"và tôi nghĩ rằng phương pháp đó cũng sẽ hoạt động tốt.

Cách tiếp cận này không hoạt động nếu bạn đang phát triển bằng Máy chủ phát triển Visual Studio (Cassini, máy chủ Web được tích hợp vào Visual Studio). Tôi cho rằng nó sẽ hoạt động trong IIS Express, nhưng tôi chưa kiểm tra điều đó.


0

Tôi đã có cùng một vấn đề Application_Error() không bị tấn công. Tôi đã thử tất cả mọi thứ, cho đến khi cuối cùng tôi bước qua những gì đang xảy ra. Tôi đã có một số mã tùy chỉnh trong một sự kiện ELMAH đang thêm JSON vào email mà nó gửi và đã xảy ra lỗi null!

Sửa lỗi nội bộ cho phép mã tiếp tục Application_Error()sự kiện như mong đợi.

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.