2012-07-12 47 views
10

Trong một ứng dụng MVC3 web tôi đã sử dụngMVC [HandleError] HandleErrorAttribute gọi hai lần khi sử dụng khai thác gỗ toàn cầu

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

để áp dụng xử lý lỗi toàn cầu nơi người dùng được hiển thị 'Lỗi' xem nếu một ngoại lệ unhandled xảy ra.

Đối với một Chế độ xem cụ thể, tôi cũng muốn có một chế độ xem lỗi khác được hiển thị nếu ngoại lệ chưa được xử lý xảy ra bằng cách trang trí phương thức với [HandleError(View = "SpecialError")]. Điều này làm việc tốt.

Sau đó tôi muốn thêm nhật ký toàn cầu ngoại lệ chưa được xử lý. Tôi đã tạo thuộc tính HandleError tùy chỉnh có mã đăng nhập:

public class MyHandleErrorAttribute : HandleErrorAttribute 
    { 
     public override void OnException(ExceptionContext context) 
     { 
      // Write to log code 
      base.OnException(context); 
     } 
    } 

Và cập nhật RegisterGlobalPhương pháp trang trí và phương pháp để sử dụng tên thuộc tính này thay thế. Điều này hoạt động nói chung nhưng khi một ngoại lệ xảy ra trong phương thức được trang trí bằng MyHandleError(View = "SpecialError")], phương pháp OnException được gọi là hai lần. Ban đầu tôi cho rằng việc trang trí phương thức với thuộc tính này thay thế cho trình xử lý toàn cục, nhưng có vẻ như nó được thêm vào (điều này có ý nghĩa hơn, nhưng nó không phải là thứ tôi muốn). Bằng cách gọi OnException hai lần, cùng một ngoại lệ được ghi hai lần mà không phải xảy ra. Tôi không nghĩ rằng OnException đang được gọi hai lần bởi vì nó là thuộc tính tùy chỉnh - tôi tin rằng điều này xảy ra với thuộc tính HandleError chuẩn cũng có thể hiển thị khi tôi đang tạo bản ghi của nó.

Cuối cùng, tôi muốn ghi lại tất cả các ngoại lệ chưa được xử lý (một lần), trong khi giữ lại các tính năng được cung cấp bởi [HandleError], đặc biệt là thiết lập các khung nhìn khác nhau cho các ngoại lệ của phương thức cụ thể. Có cách nào để làm việc này không?

Trả lời

9

Tôi tin rằng tôi tìm thấy một giải pháp sạch cho bản thân mình. Mở rộng HandleError có vẻ như là một ý tưởng hay nhưng bây giờ tôi nghĩ đó là một bước đi sai hướng. Tôi không muốn xử lý bất kỳ lỗi nào khác nhau, chỉ cần viết ngoại lệ để đăng nhập một lần trước khi HandleError chọn chúng. Bởi vì điều này, HandleError mặc định có thể được giữ nguyên tại chỗ. Mặc dù OnException có thể được gọi nhiều lần, nó dường như hoàn toàn lành tính trong việc thực hiện tiêu chuẩn của HandleErrorAttribute.

Thay vào đó tôi đã tạo một bộ lọc ngoại lệ logging:

public class LoggedExceptionFilter : IExceptionFilter 
    { 
     public void OnException(ExceptionContext filterContext) 
     { 
      // logging code 
     } 
    } 

Nó không cần quá thừa kế từ FilterAttribute vì nó chỉ được đăng ký một lần trong vòng RegisterGlobalFilters cùng HandleErrorAttribute.

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

này cho phép ngoại lệ được ghi gọn gàng mà không thay đổi [HandleError] tính năng tiêu chuẩn

+1

Cảm ơn! cái này cũng có tác dụng với tôi. – abjbhat

+0

Xin chào, giải pháp tuyệt vời, nhưng làm cách nào bạn có thể nhận thêm thông tin từ nguồn ngoại lệ như tên lớp/phương thức? – Patrick

2

Bạn có thể tạo một phong tục IFilterProvider mà sẽ kiểm tra nếu bộ lọc đã được áp dụng hành động đó:

public class MyFilterProvider : IFilterProvider 
{ 
    public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor) 
    { 
     if (!actionDescriptor.GetFilterAttributes(true).Any(a => a.GetType() == typeof(MyHandleErrorAttribute))) 
     { 
      yield return new Filter(new MyHandleErrorAttribute(), FilterScope.Global, null); 
     } 
    } 
} 

Sau đó, thay vì đăng ký bộ lọc của bạn với GlobalFilterCollection, bạn sẽ đăng ký cung cấp bộ lọc của bạn trong Application_Start()

FilterProviders.Providers.Add(new MyFilterProvider()); 

Ngoài ra (tương tự như những gì @ Mark đề nghị), bạn có thể thiết lập một cách rõ ràng ExceptionHandled tài sản của ExceptionContext

public class MyHandleErrorAttribute : HandleErrorAttribute 
{ 
    public override void OnException(ExceptionContext context) 
    { 
     if(context.ExceptionHandled) return; 

     // Write to log code 
     base.OnException(context); 
     context.ExceptionHandled = true; 
    } 
} 
3

Hãy thử điều này,

public class MyHandleErrorAttribute : HandleErrorAttribute 
{ 
    public override void OnException(ExceptionContext context) 
    { 
     var exceptionHandled = context.ExceptionHandled; 

     base.OnException(context);       

     if(!exceptionHandled && context.ExceptionHandled) 
      // log the error. 
    } 
} 
+0

Bí quyết hoàn hảo là gì. Cảm ơn nó hoạt động tốt cho tôi. Cảm ơn – mayk

-1

Tôi thực sự tìm thấy giải pháp cho việc giữ phương pháp OnException từ bắn hai lần. Nếu bạn đang sử dụng Trong FilterConfig.RegisterGlobalFilters() phương pháp, bình luận ra các tên đăng ký lưu của HandleErrorAttribute:

public class FilterConfig 
{ 
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
    { 
     //filters.Add(new HandleErrorAttribute()); 
    } 
} 

Trong thực tế, tôi cũng sử dụng được xây dựng trong HandleErrorAttribute mà không cần đăng ký nó, và nó làm việc tốt. Tôi chỉ cần bật các lỗi tùy chỉnh:

<system.web> 
    <customErrors mode="On" /> 
</system.web> 
+0

Có lẽ bạn nên đọc lại câu hỏi. Anh ấy muốn sử dụng cả hai cùng một lúc. –

Các vấn đề liên quan