全局记录来自 ASP.NET [ScriptService] 服务的异常

Posted

技术标签:

【中文标题】全局记录来自 ASP.NET [ScriptService] 服务的异常【英文标题】:Globally log exceptions from ASP.NET [ScriptService] services 【发布时间】:2010-10-03 06:37:52 【问题描述】:

我正在使用 [System.Web.Script.Services.ScriptService] 标记来使用可从客户端 javascript 调用的 Web 服务。我需要的是一种在这些方法中全局记录任何未处理异常的方法。在客户端,我收到错误回调并可以从那里继续,但我需要一个服务器端捕获来记录异常。

这个网址的人: http://ayende.com/Blog/archive/2008/01/06/ASP.Net-Ajax-Error-Handling-and-WTF.aspx

建议不能这样做。

准确吗?我真的必须去整个系统中的每一个网络方法并尝试/捕获整个方法吗?

【问题讨论】:

【参考方案1】:

您可以使用 HTTP 模块来捕获 Web 服务方法抛出的异常消息、堆栈跟踪和异常类型。

首先是一些背景...

如果 Web 服务方法引发异常,则 HTTP 响应的状态代码为 500。

如果自定义错误关闭,则 Web 服务将返回异常 到客户端的消息和堆栈跟踪 作为 JSON。例如:"Message":"Exception message","StackTrace":" at WebApplication.HelloService.HelloWorld() in C:\Projects\*** Examples\WebApplication\WebApplication\HelloService.asmx.cs:line 22","ExceptionType":"System.ApplicationException"

当自定义错误出现时, Web 服务返回默认消息 到客户端并删除堆栈 跟踪和异常类型:"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""

所以我们需要为 Web 服务设置自定义错误并插入一个 HTTP 模块:

    检查请求是否针对 Web 服务方法 检查是否抛出异常 - 即返回状态码 500 如果 1) 和 2) 为真,则获取将发送到客户端的原始 JSON 并将其替换为默认 JSON

以下代码是执行此操作的 HTTP 模块示例:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;

public class ErrorHandlerModule : IHttpModule 

  public void Init(HttpApplication context) 
    context.PostRequestHandlerExecute += OnPostRequestHandlerExecute;
    context.EndRequest += OnEndRequest;
  

  static void OnPostRequestHandlerExecute(object sender, EventArgs e) 
    HttpApplication context = (HttpApplication) sender;
    // TODO: Update with the correct check for your application
    if (context.Request.Path.StartsWith("/HelloService.asmx") 
        && context.Response.StatusCode == 500) 
      context.Response.Filter = 
        new ErrorHandlerFilter(context.Response.Filter);
      context.EndRequest += OnEndRequest;
    
  

  static void OnEndRequest(object sender, EventArgs e) 
    HttpApplication context = (HttpApplication) sender;
    ErrorHandlerFilter errorHandlerFilter = 
      context.Response.Filter as ErrorHandlerFilter;
    if (errorHandlerFilter == null) 
      return;
    

    string originalContent =
      Encoding.UTF8.GetString(
        errorHandlerFilter.OriginalBytesWritten.ToArray());

    // If customErrors are Off then originalContent will contain JSON with
    // the original exception message, stack trace and exception type.

    // TODO: log the exception
  

  public void Dispose()  

该模块使用以下过滤器覆盖发送给客户端的内容并存储原始字节(包含异常消息、堆栈跟踪和异常类型):

public class ErrorHandlerFilter : Stream 

  private readonly Stream _responseFilter;

  public List OriginalBytesWritten  get; private set; 

  private const string Content = 
    "\"Message\":\"There was an error processing the request.\"" +
    ",\"StackTrace\":\"\",\"ExceptionType\":\"\"";

  public ErrorHandlerFilter(Stream responseFilter) 
    _responseFilter = responseFilter;
    OriginalBytesWritten = new List();
  

  public override void Flush() 
    byte[] bytes = Encoding.UTF8.GetBytes(Content);
    _responseFilter.Write(bytes, 0, bytes.Length);
    _responseFilter.Flush();
  

  public override long Seek(long offset, SeekOrigin origin) 
    return _responseFilter.Seek(offset, origin);
  

  public override void SetLength(long value) 
    _responseFilter.SetLength(value);
  

  public override int Read(byte[] buffer, int offset, int count) 
    return _responseFilter.Read(buffer, offset, count);
  

  public override void Write(byte[] buffer, int offset, int count) 
    for (int i = offset; i < offset + count; i++) 
      OriginalBytesWritten.Add(buffer[i]);
    
  

  public override bool CanRead 
    get  return _responseFilter.CanRead; 
  

  public override bool CanSeek 
    get  return _responseFilter.CanSeek; 
  

  public override bool CanWrite 
    get  return _responseFilter.CanWrite; 
  

  public override long Length 
    get  return _responseFilter.Length; 
  

  public override long Position 
    get  return _responseFilter.Position; 
    set  _responseFilter.Position = value; 
  

此方法需要为 Web 服务关闭自定义错误。您可能希望为应用程序的其余部分保留自定义错误,因此应将 Web 服务放置在子目录中。只能使用覆盖父设置的 web.config 来关闭该目录中的自定义错误。

【讨论】:

虽然我没有在我当前的项目中执行此操作,但这可能是可靠地捕获所有这些错误的唯一方法。 我试过这个,但不知何故,我的 context.Response.Filter.length 抛出了“System.NotSupportedException”类型的异常。它只是行不通。我认为它非常接近,我的输出与您所说的完全一样。我得到了 500 个状态码。【参考方案2】:

您在后端运行存储过程。然后,对于单个变量,它返回超过 1 个值。因此,会发生冲突,并引发此错误。

【讨论】:

【参考方案3】:

我知道这并不能回答每个问题,但我前段时间自己去寻找这个问题,结果却空手而归。最终将每个 Web 服务调用包装在 try/catch 中,然后 catch 调用我们的错误记录器。糟透了,但它有效。

【讨论】:

【参考方案4】:

在 ASP.Net 中,可以使用 global error handler 捕获所有运行处理的异常,尽管博客文章建议这不起作用,但您可以尝试使用这种方法尝试以某种方式重新引发错误?

另一个想法是查看 ASP.Net 的开源 elmah(错误记录模块和处理程序),这可能会有所帮助,或者该社区中的某个人可能有想法。

【讨论】:

是的,我不确定您是否真正理解了这个问题。异常 is 被 ASP.NET 脚本服务代码捕获——在我有机会捕获它之前捕获。所以全局错误处理程序不起作用。框架在客户端发送一个错误,所以我可以显示一条消息,但没有服务器挂钩 不过,elmah 链接很有趣……我一定会去看看 我要第二个 ELMAH。我自己正是为了这个目的而使用它。 ELMAH 不起作用,因为它使用 [ScriptServer] 属性抑制的全局错误处理程序。

以上是关于全局记录来自 ASP.NET [ScriptService] 服务的异常的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET全局错误处理和异常日志记录以及IIS配置自定义错误页面

使用 NLog 在 ASP.NET Web API 2.1 中进行全局异常处理?

ASP.Net Core 1 日志记录错误 - 找不到来自源应用程序的事件 ID xxxx 的描述

在Asp.Net Core中使用ModelConvention实现全局过滤器隔离

c# asp.net GridView中如何删除一条记录后刷新页面

使用中间件的全局异常处理在 ASP.Net Core 应用程序中不起作用