2009-05-01 39 views
63

Làm cách nào để xử lý chính xác các ngoại lệ được ném từ bộ điều khiển trong ASP.NET MVC? Thuộc tính HandleError dường như chỉ xử lý các ngoại lệ được ném bởi cơ sở hạ tầng MVC và không ngoại lệ được ném bởi mã của riêng tôi.Xử lý lỗi trong ASP.NET MVC

Sử dụng web.config

<customErrors mode="On"> 
    <error statusCode="401" redirect="/Errors/Http401" /> 
</customErrors> 

này với đoạn mã sau

namespace MvcApplication1.Controllers 
{ 
    [HandleError] 
    public class HomeController : Controller 
    { 
     public ActionResult Index() 
     { 
      // Force a 401 exception for testing 
      throw new HttpException(401, "Unauthorized"); 
     } 
    } 
} 

không dẫn đến những gì tôi đã hy vọng. Thay vào đó tôi nhận được trang ASP.NET lỗi chung nói với tôi để sửa đổi web.config của tôi để xem thông tin lỗi thực tế. Tuy nhiên, nếu thay vì ném một ngoại lệ tôi trở lại một Xem không hợp lệ, tôi nhận được trang /Shared/Views/Error.aspx:

return View("DoesNotExist"); 

Ném ngoại lệ trong một bộ điều khiển giống như tôi đã làm ở trên có vẻ để vượt qua tất cả chức năng HandleError, vậy cách nào đúng để tạo các trang lỗi và làm thế nào để tôi chơi tốt với cơ sở hạ tầng MVC?

Trả lời

20

Nhờ kazimanzurrashaid, đây là những gì tôi lên vết thương làm trong Global.asax.cs:

protected void Application_Error() 
{ 
    Exception unhandledException = Server.GetLastError(); 
    HttpException httpException = unhandledException as HttpException; 
    if (httpException == null) 
    { 
     Exception innerException = unhandledException.InnerException; 
     httpException = innerException as HttpException; 
    } 

    if (httpException != null) 
    { 
     int httpCode = httpException.GetHttpCode(); 
     switch (httpCode) 
     { 
      case (int) HttpStatusCode.Unauthorized: 
       Response.Redirect("/Http/Error401"); 
       break; 
     } 
    } 
} 

Tôi sẽ có thể thêm nhiều trang hơn vào HttpContoller dựa trên bất kỳ mã lỗi HTTP bổ sung nào mà tôi cần hỗ trợ.

+1

Tôi sẽ đề nghị bạn kiểm tra nguồn của http://kigg.codeplex.com nơi bạn sẽ tìm thấy mã nguồn hoàn chỉnh của httpModule này, tôi chỉ muốn lộn xộn global.asax đó là lý do tại sao HttpModule. – kazimanzurrashid

13

Thuộc tính HandleError dường như chỉ xử lý ngoại lệ do cơ sở hạ tầng MVC ném và không ngoại lệ được ném bởi mã của riêng tôi.

Điều đó không đúng. Thật vậy, HandleError sẽ chỉ "xử lý" ngoại lệ hoặc được ném vào mã của riêng bạn hoặc trong mã được gọi bằng mã của riêng bạn. Nói cách khác, chỉ có trường hợp ngoại lệ trong đó hành động của bạn nằm trong ngăn xếp cuộc gọi.

Giải thích thực sự về hành vi bạn đang xem là ngoại lệ cụ thể mà bạn đang ném. HandleError hoạt động khác với một HttpException. Từ mã nguồn:

 // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method), 
     // ignore it. 
     if (new HttpException(null, exception).GetHttpCode() != 500) { 
      return; 
     } 
+0

Bạn đã chỉ ra rằng một trong những phát biểu của tôi là không chính xác mà không giải quyết các vấn đề thực sự của việc không thể cung cấp xử lý lỗi cho HTTP mã lỗi. –

+4

Tôi có thể giải thích những gì đang xảy ra, nhưng tôi sẽ cần thêm thông tin để cho bạn biết điều cần làm cho ứng dụng của bạn. HandleError hoạt động trên một hành động, và bạn thường không ném HttpExceptions trong một hành động. Thay vì đoán những gì bạn đang thực sự cố gắng để làm và cung cấp cho bạn lời khuyên có khả năng không chính xác, tôi chỉ có thể giải thích sự thật đằng sau hành vi mà bạn đang nhìn thấy. Vui lòng cập nhật câu hỏi nếu bạn muốn giải thích thêm về những gì bạn thực sự muốn làm. –

63

Controller.OnException(ExceptionContext context). Ghi đè nó.

protected override void OnException(ExceptionContext filterContext) 
{ 
    // Bail if we can't do anything; app will crash. 
    if (filterContext == null) 
     return; 
     // since we're handling this, log to elmah 

    var ex = filterContext.Exception ?? new Exception("No further information exists."); 
    LogException(ex); 

    filterContext.ExceptionHandled = true; 
    var data = new ErrorPresentation 
     { 
      ErrorMessage = HttpUtility.HtmlEncode(ex.Message), 
      TheException = ex, 
      ShowMessage = !(filterContext.Exception == null), 
      ShowLink = false 
     }; 
    filterContext.Result = View("ErrorPage", data); 
} 
+0

Vâng, đây là cách để làm điều đó. Sử dụng một trong các bộ lọc được cung cấp hoặc tạo bộ lọc ngoại lệ của riêng bạn là cách để đi. Bạn có thể thay đổi chế độ xem dựa trên loại ngoại lệ hoặc đi sâu hơn bằng cách kiểm tra thêm thông tin trong bộ lọc tùy chỉnh của mình. – a7drew

+0

Bạn có chắc đây không phải là bản phát hành trước của MVC nhưng đã được thay đổi cho 1.0? Tôi không thấy ghi đè này, thay vào đó có vẻ như bạn nên ghi đè OnActionExecuted và kiểm tra giá trị filterContext.Exception –

+0

http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onexception (VS100) .aspx – Will

6

Tôi không nghĩ rằng bạn sẽ có thể hiển thị ErrorPage cụ thể dựa trên HttpCode với thuộc tính HandleError và tôi muốn sử dụng một HttpModule cho mục đích này. Giả sử rằng tôi có thư mục "ErrorPages" nơi trang khác nhau tồn tại cho từng lỗi cụ thể và ánh xạ được định rõ trong web.config giống như ứng dụng biểu mẫu web thông thường. Và sau đây là các mã được sử dụng để hiển thị các trang báo lỗi:

public class ErrorHandler : BaseHttpModule{ 

public override void OnError(HttpContextBase context) 
{ 
    Exception e = context.Server.GetLastError().GetBaseException(); 
    HttpException httpException = e as HttpException; 
    int statusCode = (int) HttpStatusCode.InternalServerError; 

    // Skip Page Not Found and Service not unavailable from logging 
    if (httpException != null) 
    { 
     statusCode = httpException.GetHttpCode(); 

     if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable)) 
     { 
      Log.Exception(e); 
     } 
    } 

    string redirectUrl = null; 

    if (context.IsCustomErrorEnabled) 
    { 
     CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors"); 

     if (section != null) 
     { 
      redirectUrl = section.DefaultRedirect; 

      if (httpException != null) 
      { 
       if (section.Errors.Count > 0) 
       { 
        CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)]; 

        if (item != null) 
        { 
         redirectUrl = item.Redirect; 
        } 
       } 
      } 
     } 
    } 

    context.Response.Clear(); 
    context.Response.StatusCode = statusCode; 
    context.Response.TrySkipIisCustomErrors = true; 

    context.ClearError(); 

    if (!string.IsNullOrEmpty(redirectUrl)) 
    { 
     context.Server.Transfer(redirectUrl); 
    } 
} 

}

+0

Đây là những gì tôi đang tìm kiếm.Tôi đã có một cách tiếp cận đơn giản hơn một chút và đặt mã của tôi trong Application_Error để xử lý cùng một bộ lỗi trên các bộ điều khiển. Tôi sẽ đăng giải pháp trong câu trả lời cho câu hỏi này. –

3

Một khả năng khác (không đúng trong trường hợp của bạn) mà những người khác đọc bài viết này có thể gặp là trang báo lỗi của bạn là ném một lỗi chính nó, hoặc không được thực hiện:

System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo> 

Nếu đây là trường hợp sau đó bạn sẽ nhận được trang lỗi mặc định (nếu không bạn sẽ nhận được một vòng lặp vô hạn vì nó sẽ tiếp tục cố gắng gửi chính nó đến trang lỗi tùy chỉnh của bạn).Điều này không rõ ràng với tôi.

Mô hình này là mô hình được gửi đến trang lỗi. Nếu trang lỗi của bạn sử dụng trang chính giống như phần còn lại của trang web và yêu cầu bất kỳ thông tin mô hình nào khác thì bạn sẽ cần phải tạo loại thuộc tính [HandleError] của riêng mình hoặc ghi đè OnException hoặc gì đó.

+0

Bạn cũng có thể khai báo thuộc tính [HandleError] trên các bộ điều khiển hoặc các phương thức điều khiển của bạn, nhưng nó không giúp cho các lỗi HTTP. –

2

Tôi đã chọn phương pháp Controller.OnException(), mà với tôi là lựa chọn hợp lý - vì tôi đã chọn ASP.NET MVC, tôi thích ở cấp độ khuôn khổ hơn, và tránh rối tung với các cơ chế cơ bản, nếu có thể.

Tôi chạy vào các vấn đề sau:

Nếu ngoại lệ xảy ra trong tầm nhìn, sản lượng một phần từ quan điểm cho rằng sẽ xuất hiện trên màn hình, cùng với các lỗi tin nhắn.

tôi cố định này bằng cách xóa các phản ứng, trước khi thiết lập filterContext.Result - như thế này:

 filterContext.HttpContext.Response.Clear(); // gets rid of any garbage 
     filterContext.Result = View("ErrorPage", data); 

Hy vọng điều này giúp tiết kiệm của người khác một thời gian :-)

+0

Dường như bạn đang sao chép hành vi đã được cung cấp bởi thuộc tính HandleError. –

+2

Có thể. Học tất cả những thứ này là trở thành một nỗ lực đau đớn. Rất nhiều ví dụ xấu và tài liệu lỗi thời để lọc qua. Tôi đoán tôi chỉ đóng góp một ví dụ xấu ;-) –

2
 protected override void OnException (ExceptionContext filterContext) 
    { 
     if (filterContext != null && filterContext.Exception != null) 
     { 
      filterContext.ExceptionHandled = true; 
      this.View("Error").ViewData["Exception"] = filterContext.Exception.Message; 
      this.View("Error").ExecuteResult(this.ControllerContext); 
     } 
    } 
+1

Bạn có thể lưu trữ kết quả của 'this.View (" Error ")' trong một biến cục bộ thay vì gọi phương thức 2 lần. – SandRock

1

Jeff Atwood của User Friendly Exception Handling module hoạt động tuyệt vời cho MVC. Bạn có thể cấu hình nó hoàn toàn trong web.config của bạn, không có thay đổi mã nguồn dự án MVC nào cả. Tuy nhiên, nó cần một sửa đổi nhỏ để trả lại trạng thái HTTP ban đầu chứ không phải trạng thái 200. Xem này liên quan forum post.

Về cơ bản, trong Handler.vb, bạn có thể thêm một cái gì đó như:

' In the header... 
Private _exHttpEx As HttpException = Nothing 

' At the top of Public Sub HandleException(ByVal ex As Exception)... 
HttpContext.Current.Response.StatusCode = 500 
If TypeOf ex Is HttpException Then 
    _exHttpEx = CType(ex, HttpException) 
    HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode() 
End If 
Các vấn đề liên quan