WCF服务异常的最佳实践

34

我正在开发一个分布式应用程序。其中有一些角色和权限集需要我进行验证。
在这种情况下,向客户端发送一条错误信息,比如未授权访问是否是个好的做法?
还是应该抛出一个异常


一旦您知道用户未经授权,您不希望在任何情况下继续执行任何其他代码,因此抛出异常是一个好方法。 - Rahul R.
2
抛出异常真的好吗?引用Oreilly.Programming.WCF.Services.3rd.Edition中的话: 第6章故障: “诸如异常和异常处理之类的概念是技术特定的,不应超越服务边界。此外,客户端尝试处理错误的尝试不可避免地会导致耦合度增加。”但我们公司肯定在这方面进行争论。 - schmendrick
3个回答

59

在您的服务操作中,您可以指定一个FaultContract,它将同时用于以下两个目的:

[OperationContract]
[FaultContract(typeof(MyServiceFault))]
void MyServiceOperation();

请注意,MyServiceFault 必须使用 DataContract 和 DataMember 属性进行标记,就像你处理复杂类型时一样:

[DataContract]
public class MyServiceFault
{
    private string _message;

    public MyServiceFault(string message)
    {
        _message = message;
    }

    [DataMember]
    public string Message { get { return _message; } set { _message = value; } }
}

在服务端,您可以:

throw new FaultException<MyServiceFault>(new MyServiceFault("Unauthorized Access"));

并且在客户端:

try
{
    ...
}
catch (FaultException<MyServiceFault> fault)
{
    // fault.Detail.Message contains "Unauthorized Access"
}

7
针对OP:你不想“抛出异常”,而是要“返回错误”。 这个答案会告诉你如何做到这一点。 - John Saunders
我个人认为这是最优雅的方法。委托给WCF来展示如何处理异常。谢谢。 - marcelo-ferraz

13

在WCF服务实现方法中,您可以捕获所有异常并将它们重新抛出为FaultExceptions。通过这种方式,异常将会在客户端上重新抛出,并且您可以选择自己的消息作为提示:

[OperationContract]
public List<Customer> GetAllCustomers()
{
    try
    {
        ... code to retrieve customers from datastore
    }
    catch (Exception ex)
    {
        // Log the exception including stacktrace
        _log.Error(ex.ToString());

        // No stacktrace to client, just message...
        throw new FaultException(ex.Message);
    }
}

为了避免不期而至的错误被传递回客户端,良好的实践是在服务器端的代码中永远不要抛出异常实例。相反,创建一个或多个自己的异常类型并将它们抛出。这样做可以区分意外的服务器处理错误和由于无效请求等原因引发的错误:
public List<Customer> GetAllCustomers()
{
    try
    {
        ... code to retrieve customers from datastore
    }
    catch (MyBaseException ex)
    {
         // This is an error thrown in code, don't bother logging it but relay
         // the message to the client.
         throw new FaultException(ex.Message);
    }
    catch (Exception ex)
    {
        // This is an unexpected error, we need log details for debugging
        _log.Error(ex.ToString());

        // and we don't want to reveal any details to the client
        throw new FaultException("Server processing error!");
    }
}

@Yakov:来自您选择的日志框架的一个类。日志代码主要用于说明如何针对不同类型的错误进行不同的记录。如果您正在寻找一个好的日志框架,请看看log4net。非常成熟且易于使用。 - Daniel Persson
2
有人能否解释一下为什么这个答案没有更多点赞,为什么被采纳的答案更好?它更复杂但却实现了同样的功能? - Esko
3
据我理解,这些答案都使用了相同的功能:通过抛出 FaultException 来生成 SOAP-Fault。唯一的区别是,这个答案仅仅抛出了基本的 FaultException,而接受的答案展示了如何自定义 FaultException。自定义使你能够更具体地处理不同类型的服务器端异常并在客户端传递更多信息。可以将抛出 FaultException 与抛出 FaultException<MyServiceFault> 进行比较,就像在非 WCF 上下文中抛出 Exception 与抛出 MyCustomException 一样。 - germanSharper

3

如果您不使用 basicHTTPBinding,抛出一般的Dot Net异常会导致服务客户端代理和服务器通道进入故障状态...为避免这种情况,您应该始终从服务中抛出 FaultException...

只需在您的 catch 块中使用:

throw new FaultException("Your message to the clients");

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