2010-08-28 20 views
15

Tôi muốn bẫy bất kỳ ngoại lệ không được giải quyết nào được ném vào một dịch vụ web ASP.NET, nhưng không có gì tôi đã thử đã làm việc cho đến nay.Có cách nào để bẫy tất cả các lỗi trong dịch vụ web AJAX không?

Trước hết, sự kiện HttpApplication.Error không bắn vào các dịch vụ web, vì vậy đó là ra ..

Phương pháp tiếp theo là để thực hiện một phần mở rộng xà phòng, và thêm nó vào web.config với:

<soapExtensionTypes> 
    <add type="Foo" priority="1" group="0" /> 
</soapExtensionTypes> 

Tuy nhiên, điều này không có tác dụng nếu bạn gọi phương thức web trên JSON (mà trang web của tôi không độc quyền) ..

ý tưởng tiếp theo của tôi sẽ được để viết riêng tôi HttpHandler cho .asmx, mà sẽ hy vọng có được từ System.Web.Script.Services.ScriptHandlerFac tory và làm một cái gì đó thông minh. Tôi chưa thử cái này.

Tôi có thiếu phương pháp tiếp cận nào không? Cảm ơn!

Mike

UPDATE:

tôi sẽ tóm tắt các giải pháp có thể ở đây:

1) Nâng cấp lên WCF mà làm cho toàn bộ điều này nhiều, dễ dàng hơn nhiều.

2) Vì bạn không thể phân lớp hoặc ghi đè lớp RestHandler, bạn sẽ phải triển khai lại toàn bộ thứ như IHttpHandler của riêng bạn hoặc sử dụng sự phản chiếu để gọi thủ công vào các phương thức của nó. Vì nguồn của RestHandler là công khai và chỉ dài khoảng 500 dòng, nên phiên bản của riêng bạn có thể không phải là một lượng lớn công việc nhưng bạn có trách nhiệm duy trì nó. Tôi cũng không biết về bất kỳ hạn chế cấp phép nào liên quan đến mã này.

3) Bạn có thể bọc các phương thức của bạn trong các khối try/catch hoặc có thể sử dụng các biểu thức LAMBDA để làm cho mã này sạch hơn một chút. Nó vẫn sẽ yêu cầu bạn phải sửa đổi từng phương pháp trong dịch vụ web của bạn.

+0

Có bất kỳ linh hoạt trong yêu cầu mà bạn không thể sửa đổi các phương pháp web? –

+0

Vâng, vâng - miễn là tôi không phải bao quanh tất cả chúng trong các khối try/catch khổng lồ .. Nếu giải pháp được sạch sẽ, sau đó tôi mở cửa cho nó. –

+0

Tôi có một giải pháp yêu cầu khối Thử và bắt một dòng. Bạn có thể cải thiện nó để loại bỏ hoàn toàn thử/nắm bắt, tôi chưa đào sâu. Bạn muốn đăng nó? –

Trả lời

11

Như được mô tả trong Capture all unhandled exceptions automatically with WebService thực sự không có giải pháp tốt.

Lý do bạn không thể chụp HttpApplication.Error, v.v. có liên quan đến cách RestHandler đã được thực hiện bởi những người tốt ở Microsoft. Cụ thể, RestHandler bắt một cách rõ ràng (xử lý) các ngoại lệ và viết ra các chi tiết ngoại lệ đối với đáp ứng:

internal static void ExecuteWebServiceCall(HttpContext context, WebServiceMethodData methodData) 
{ 
    try 
    { 
     NamedPermissionSet namedPermissionSet = HttpRuntime.NamedPermissionSet; 
     if (namedPermissionSet != null) 
     { 
      namedPermissionSet.PermitOnly(); 
     } 
     IDictionary<string, object> rawParams = GetRawParams(methodData, context); 
     InvokeMethod(context, methodData, rawParams); 
    } 
    catch (Exception exception) 
    { 
     WriteExceptionJsonString(context, exception); 
    } 
} 

Để làm cho vấn đề tồi tệ hơn, không có điểm mở rộng sạch (mà tôi có thể tìm thấy), nơi bạn có thể thay đổi/mở rộng hành vi. Nếu bạn muốn đi xuống con đường viết IHttpHandler của riêng bạn, tôi tin rằng bạn sẽ phải thực hiện lại phần còn lại của RestHandler (hoặc RestHandlerWithSession); bất kể Reflector sẽ là bạn của bạn.

Đối với những người có thể chọn để thay đổi WebMethods họ

Nếu bạn đang sử dụng Visual Studio 2008 hoặc mới hơn, sử dụng biểu thức Lambda làm cho mọi thứ không quá xấu (mặc dù không phải toàn cầu/generic giải pháp) về hoặc loại bỏ nhân đôi mã.

[WebMethod] 
[ScriptMethod(UseHttpGet = true, ResponseFormat = ResponseFormat.Json)] 
public String GetServerTime() 
{ 
    return Execute(() => DateTime.Now.ToString()); 
} 

public T Execute<T>(Func<T> action) 
{ 
    if (action == null) 
    throw new ArgumentNullException("action"); 

    try 
    { 
    return action.Invoke(); 
    } 
    catch (Exception ex) 
    { 
    throw; // Do meaningful error handling/logging... 
    } 
} 

Nơi thực thi có thể được triển khai trong lớp con của WebService hoặc làm phương pháp mở rộng.

UPDATE: Reflection Ác

Như đã đề cập trong câu trả lời origional của tôi, bạn có thể lạm dụng phản ánh để có được những gì bạn muốn ... đặc biệt bạn có thể tạo HttpHandler của riêng bạn mà làm cho việc sử dụng bên trong của RestHandler để cung cấp một điểm chặn để ghi lại các chi tiết ngoại lệ. Tôi đã bao gồm một ví dụ mã "không an toàn" bên dưới để giúp bạn bắt đầu.

Cá nhân, tôi sẽ KHÔNG sử dụng mã này; nhưng nó đã có tác dụng.

namespace WebHackery 
{ 
    public class AjaxServiceHandler : IHttpHandler 
    { 
    private readonly Type _restHandlerType; 
    private readonly MethodInfo _createHandler; 
    private readonly MethodInfo _getRawParams; 
    private readonly MethodInfo _invokeMethod; 
    private readonly MethodInfo _writeExceptionJsonString; 
    private readonly FieldInfo _webServiceMethodData; 

    public AjaxServiceHandler() 
    { 
     _restHandlerType = typeof(ScriptMethodAttribute).Assembly.GetType("System.Web.Script.Services.RestHandler"); 

     _createHandler = _restHandlerType.GetMethod("CreateHandler", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(HttpContext) }, null); 
     _getRawParams = _restHandlerType.GetMethod("GetRawParams", BindingFlags.NonPublic | BindingFlags.Static); 
     _invokeMethod = _restHandlerType.GetMethod("InvokeMethod", BindingFlags.NonPublic | BindingFlags.Static); 
     _writeExceptionJsonString = _restHandlerType.GetMethod("WriteExceptionJsonString", BindingFlags.NonPublic | BindingFlags.Static, null, new[] { typeof(HttpContext), typeof(Exception) }, null); 

     _webServiceMethodData = _restHandlerType.GetField("_webServiceMethodData", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField); 
    } 

    public bool IsReusable 
    { 
     get { return true; } 
    } 

    public void ProcessRequest(HttpContext context) 
    { 
     var restHandler = _createHandler.Invoke(null, new Object[] { context }); 
     var methodData = _webServiceMethodData.GetValue(restHandler); 
     var rawParams = _getRawParams.Invoke(null, new[] { methodData, context }); 

     try 
     { 
     _invokeMethod.Invoke(null, new[] { context, methodData, rawParams }); 
     } 
     catch (Exception ex) 
     { 
     while (ex is TargetInvocationException) 
      ex = ex.InnerException; 

     // Insert Custom Error Handling HERE... 

     _writeExceptionJsonString.Invoke(null, new Object[] { context, ex}); 
     } 
    } 
    } 
} 
+0

Yea Tôi nghĩ rằng tôi có thể xem xét mã và xem tôi có thể viết một HttpHandler rằng "kết thúc tốt đẹp" trình xử lý hiện có theo một cách nào đó không. Khác hơn thế, tôi nghĩ rằng không có giải pháp sạch sẽ và tôi chỉ nên thêm xử lý lỗi cho các phương pháp web phức tạp hơn. 1 cho câu trả lời chi tiết của bạn! –

+0

Do cách RestHandler đã được triển khai, lựa chọn duy nhất của bạn cho tiện ích mở rộng có khả năng sẽ bị phản ánh Sự lạm dụng (giả sử hoàn toàn tin tưởng) vì mọi thứ trong RestHandler là nội bộ hoặc riêng tư. –

+0

Eh đi con số .. Tự hỏi nếu bạn có thể viết một số loại HttpFilter phát hiện SOAP trường hợp ngoại lệ trong dòng phản ứng và đăng nhập chúng vào cơ sở dữ liệu. Thực sự tất cả những gì tôi muốn làm ở đây là lỗi đăng nhập. Ngay bây giờ tôi bẫy ngoại lệ trong Javascript và sau đó gọi lại một webmethod khác để đăng nhập lỗi. –

4

Đây là một câu hỏi hay. Tôi đã gặp sự cố này trong ứng dụng web mà tôi đang thực hiện. Tôi sợ giải pháp của tôi không thông minh theo bất kỳ cách nào. Tôi chỉ đơn giản là gói trong mã trong mỗi phương pháp dịch vụ web trong một try/catch và trả về một đối tượng mà chỉ ra cho dù phương pháp đã chạy thành công hay không. May mắn thay, ứng dụng của tôi không có số lượng lớn các cuộc gọi dịch vụ web để tôi có thể thoát khỏi điều này. Tôi đánh giá cao nó không phải là một giải pháp tốt nếu bạn đang làm vô số các cuộc gọi dịch vụ web.

+0

Tôi làm điều tương tự trong ứng dụng web mới nhất mà tôi đang thực hiện. Tôi có rất nhiều cuộc gọi ajax thông qua jquery và khi phương thức phía máy chủ ném một ngoại lệ tôi bắt nó, ăn mặc JSON và có một phương thức phía khách hàng toàn cầu thông báo cho người dùng có vấn đề và chi tiết của vấn đề đó là gì. –

2

Bạn đã lấy một cái nhìn tại WCF Web API? Điều này hiện đang được thử nghiệm, nhưng nó đã được sử dụng trong một số trang web trực tiếp. Phơi bày phần còn lại của json/xml api rất dễ dàng. Trong đó bạn có thể tạo lớp dẫn xuất HttpErrorHandler của riêng bạn để xử lý lỗi. Đó là một giải pháp rất sạch sẽ. Có thể đáng xem xét kỹ hơn cho bạn. Việc di chuyển bất kỳ dịch vụ hiện có nào cũng sẽ đơn giản. Hy vọng nó giúp!

+0

Điều này rất thú vị! Tôi không nghĩ rằng tôi muốn sử dụng nó trong sản xuất khá được nêu ra, nhưng tôi chắc chắn sẽ được giữ một mắt trên này. +1 cho liên kết! –

+1

Tôi sẽ thứ hai rằng điều này là dễ dàng hơn đáng kể trong WCF thế giới; chỉ cần tạo một IErrorHandler (http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.ierrorhandler(v=vs.85).aspx) và hành vi dịch vụ liên quan và bạn đã hoàn tất. –

+0

Có âm thanh như đặt cược tốt nhất của tôi trong thời gian dài là chuyển mã của tôi sang WCF. Tự hỏi làm thế nào cứng sẽ được .. –

2

Sửa

Bạn có cố gắng để biến các lỗi xử lý tắt trong web.config và sau đó thêm một module lỗi?

<location path="Webservices" > 
    <system.web> 
    <customErrors mode="Off" /> 
    </system.web> 
</location> 

xử lý lỗi thông thường như tôi nghĩ rằng nó nên ngay cả đối với RestHandler

Bạn có thể tạo một HttpModule để bắt tất cả các lỗi và trả về kết quả tùy chỉnh. Lưu ý rằng thông báo lỗi này sẽ là phản hồi cho máy khách thay vì kết quả mong đợi từ dịch vụ web. Bạn cũng có thể ném bất kỳ tùy chỉnh ngoại lệ nào trong ứng dụng và cho phép mô-đun xử lý nó nếu ứng dụng khách trả lời hoặc thông báo cho người dùng.

Ví dụ kết quả thực hiện chính xác:

{ 
    success: true, 
    data: [ .... ], 
    error: null 
} 

Ví dụ kết quả thực hiện thất bại:

{ 
    success: false, 
    data: null, 
    error: { 
    message: 'error message', 
    stackTrace: 'url encoded stack trace' 
    } 
} 

Thêm phần này vào web.config:

<modules> 
    <add name="UnhandledException" type="Modules.Log.UnhandledException"/> 
</modules> 

Bạn có thể dễ dàng thêm mã để làm cho mô-đun tự đăng ký bằng cách triển khai không được đăng ký tài liệu Hàm PreApplicationStartMethod trong mô-đun và tạo và sử dụng lớp HttpApplication tùy chỉnh của bạn để sử dụng thay cho lớp chuẩn.Bằng cách đó, bạn có thể thêm bất kỳ mô đun nào vào ứng dụng bằng cách thêm nó vào thư mục bin của ứng dụng. Đó là cách tôi làm tất cả các ứng dụng của tôi hiện tại và tôi phải nói rằng nó hoạt động hoàn hảo.

Dưới mã sẽ bắt tất cả các lỗi ứng dụng và trả lại một phản ứng html với thông báo lỗi (bạn sẽ cần phải thay thế những thứ html với thực hiện thông báo lỗi json của riêng bạn):

using System.Web; 

namespace Modules.Log 
{ 
    using System; 
    using System.Text; 

    public class LogModule : IHttpModule 
    { 
     private bool initialized = false; 
     private object initLock = new Object(); 
     private static string applicationName = string.Format("Server: {0}; [LogModule_AppName] installation name is not set", System.Environment.MachineName); 

     public void Dispose() 
     { 
     } 

     public void Init(HttpApplication context) 
     { 
      // Do this one time for each AppDomain. 
      if (!initialized) 
      { 
       lock (initLock) 
       { 
        if (!initialized) 
        { 
         context.Error += new EventHandler(this.OnUnhandledApplicationException); 

         initialized = true; 
        } 
       } 
      } 
     } 

     private void OnUnhandledApplicationException(object sender, EventArgs e) 
     { 
      StringBuilder message = new StringBuilder("<html><head><style>" + 
       "body, table { font-size: 12px; font-family: Arial, sans-serif; }\r\n" + 
       "table tr td { padding: 4px; }\r\n" + 
       ".header { font-weight: 900; font-size: 14px; color: #fff; background-color: #2b4e74; }\r\n" + 
       ".header2 { font-weight: 900; background-color: #c0c0c0; }\r\n" + 
       "</style></head><body><table><tr><td class=\"header\">\r\n\r\nUnhandled Exception logged by LogModule.dll:\r\n\r\nappId="); 

      string appId = (string)AppDomain.CurrentDomain.GetData(".appId"); 
      if (appId != null) 
      { 
       message.Append(appId); 
      } 

      message.Append("</td></tr>"); 

      HttpServerUtility server = HttpContext.Current.Server; 
      Exception currentException = server.GetLastError(); 

      if (currentException != null) 
      { 
       message.AppendFormat(
         "<tr><td class=\"header2\">TYPE</td></tr><tr><td>{0}</td></tr><tr><td class=\"header2\">REQUEST</td></tr><tr><td>{3}</td></tr><tr><td class=\"header2\">MESSAGE</td></tr><tr><td>{1}</td></tr><tr><td class=\"header2\">STACK TRACE</td></tr><tr><td>{2}</td></tr>", 
         currentException.GetType().FullName, 
         currentException.Message, 
         currentException.StackTrace, 
         HttpContext.Current != null ? HttpContext.Current.Request.FilePath : "n/a"); 
       server.ClearError(); 
      } 

      message.Append("</table></body></html>"); 

      HttpContext.Current.Response.Write(message.ToString()); 
      server.ClearError(); 
     } 
    } 
} 
+0

Các cuộc gọi Ajax đến các phương pháp web không ném ngoại lệ không được giải quyết (xem đoạn mã đầu tiên trong câu trả lời của tôi).Cách tiếp cận này hoạt động cho các cuộc gọi thông thường, nhưng RestHandler loại bỏ điều này như là một tùy chọn khi ngoại lệ được "xử lý". –

+0

Ah, bỏ lỡ ... cập nhật bài đăng với một thứ khác có thể hoạt động ... – Asken

+0

Tôi nghĩ @CalgaryCoder tổng hợp khá tốt - Ngoại lệ bị bắt và sau đó 'WriteExceptionJsonString' được gọi để ghi ra thông tin ngoại lệ được tuần tự hóa dưới dạng JSON chuỗi. Bạn sẽ phải đánh chặn điều này để có may mắn. Đó là lame họ đã không làm cho các lớp học mở rộng, nó sẽ là một số thay đổi thiết kế khá thẳng về phía trước để thêm một thế giới của nhiều quyền lực hơn. –

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