WCF REST服务-通用异常处理

9
我有大量的遗留代码,现在它是一个WCF REST服务的后端 - 如果这很重要的话,它以前是一个通常的WCF服务后端。我想实现一种机制,可以捕获任何方法中的任何异常并分析它。如果发现是已知错误,则将其处理并转换为友好的故障。
我知道我可以抛出FaultExceptionWebProtocolException而不是“通常”的异常,但是在代码中所有地方都抛出异常,并查找所有异常是非常痛苦的选项。
我试图添加一个终结点行为扩展,创建一个新的行为,覆盖标准的WebHttpBehavior.AddServerErrorHandlers 方法,并将我的错误处理程序(IErrorHandler实现)添加到终结点调度程序错误处理程序集合中。在错误处理程序中,我分析异常并根据此异常创建(或不创建)所需的故障。
我期望这个机制返回任何已知异常的自定义数据,但我错了。微软非常友好地实现了一个不可避免的WebHttpBehavior2,它无条件地将内部的Microsoft.ServiceModel.Web.WebErrorHandler添加到终结点调度程序错误处理程序集合的末尾。该处理程序忽略所有先前执行的处理程序,并仅识别一小部分异常,而大多数异常被解释为“内部服务器错误”,仅此而已。
问题是,我是否走在正确的道路上,并且是否有一种方法可以在WCF REST机制中禁用此处理程序,或者引入一个新的异常(例如,当捕获任何异常时,它首先由我的处理程序处理,如果它们抛出/返回,例如,FaultException,则将此新异常提供给Microsoft.ServiceModel.Web.WebErrorHandler,而不是原始异常)。如果我所有关于IErrorHandler和行为扩展的实验都是无用的,那么有什么其他的选择?再次强调,我真的不想修改异常抛出逻辑,只想有一个地方来捕获异常并处理它们。
非常感谢!
2个回答

8

当您将WCF SOAP服务更改为REST时,整个错误报告和处理方式都会改变。

在SOAP中,故障是您的合同的一部分。在REST中,它们只是您在HTTP响应代码和描述中输出的代码。

这里是一个catch片段:

catch (Exception e)
{
    Trace.WriteLine(e.ToString());

    OutgoingWebResponseContext response = WebOperationContext.Current.OutgoingResponse;
    response.StatusCode = System.Net.HttpStatusCode.UnsupportedMediaType; // or anything you want
    response.StatusDescription = e.Message;
    return null; // I was returning a class
}

因此,我建议您创建一个帮助程序代码,为您创建相关的错误代码,并将其放入响应中。


每个服务方法都应该添加这样的catch吗?还是有一种方法可以只写一次来处理所有异常?我喜欢基于行为的方法,因为它为所有异常定义了一个公共处理程序,我不必担心处理和处理每个单独方法的异常。最好的事情是我在端点中为所有服务定义了一个处理器。使用您的方法,我无法实现这一点,对吗? - Michael Sagalovich
1
设置“StatusDescription”没有效果!响应始终具有通用状态描述。有没有办法修复它? - Hemant
它确实有。您可以使用Fiddler检查服务器的传出消息来验证。但是,您可能会发现各种浏览器或不同的实现可能会忽略描述并只使用代码映射到预定义的消息。我在制作Android应用程序时遇到了这个问题,最终我设置了描述并更改了方法签名以返回字符串。天哪!这就是为什么我再也不会使用WCF REST了。 - Aliostad
@Aliostad,您是否曾考虑过返回自定义响应头值来表示StatusMessage?我不明白客户端不尊重为StatusDescription设置的值是WCF的错。也许我漏掉了什么? - Norman H
不完全是。这将违反HTTP协议。状态码就是状态码,尽管您可以有自定义代码,例如909“我的自定义状态”。 - Aliostad
有没有办法将StatusDescription返回给客户端?我遇到了和Hemant一样的问题。 - jnel899

2

这是我过去所做的事情

    public class MyServerBehavior : IServiceBehavior {

        public void AddBindingParameters(ServiceDescription serviceDescription,
             ServiceHostBase serviceHostBase, 
             Collection<ServiceEndpoint> endpoints, 
             BindingParameterCollection bindingParameters) {

        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription,
                                          ServiceHostBase serviceHostBase) {

            foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers) {
                chDisp.IncludeExceptionDetailInFaults = true;
                if (chDisp.ErrorHandlers.Count > 0) {
                    // Remove the System.ServiceModel.Web errorHandler
                    chDisp.ErrorHandlers.Remove(chDisp.ErrorHandlers[0]);  
                }
                // Add new custom error handler
                chDisp.ErrorHandlers.Add(new MyErrorHandler());

            }

        }

        public void Validate(ServiceDescription serviceDescription, 
                             ServiceHostBase serviceHostBase) {
        }

    }

MyErrorHandler是我实现IErrorHandler接口的类。


这看起来与我所做的非常相似。它适用于REST服务还是仅适用于SOAP?如果它适用于REST服务,那么你是如何配置服务的?如果只是针对SOAP,那么谢谢你,但就我目前所看到的而言,REST是完全不同的故事。 - Michael Sagalovich
1
@Michael 我是针对REST服务做的。我是自托管的,实际上创建了自己衍生的服务主机来设置行为。这完全基于代码,没有XML。 - Darrel Miller
自己托管,我明白了...这正是我所考虑的,但认为太复杂了。谢谢! - Michael Sagalovich

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接