您可以使用HTTP模块捕获Web服务方法抛出的异常消息、堆栈跟踪和异常类型。
首先让我们了解一些背景知识...
- 如果Web服务方法引发异常,则HTTP响应的状态代码为500。
- 如果关闭自定义错误,则Web服务将返回JSON格式的异常消息和堆栈跟踪给客户端。例如:
{"Message":"Exception message","StackTrace":" at WebApplication.HelloService.HelloWorld() in C:\Projects\Stackoverflow Examples\WebApplication\WebApplication\HelloService.asmx.cs:line 22","ExceptionType":"System.ApplicationException"}
- 当打开自定义错误时,Web服务向客户端返回默认消息,并删除堆栈跟踪和异常类型:
{"Message":"There was an error processing the request.","StackTrace":"","ExceptionType":""}
因此,我们需要关闭Web服务的自定义错误,并插入一个HTTP模块,它可以:
1. 检查请求是否针对Web服务方法
2. 检查是否抛出异常 - 即返回状态代码为500
3. 如果条件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;
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());
}
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仅在该目录中关闭自定义错误。