本文介绍一种归档WCF错误的方法,这种方法做到让服务的消费者获取最有用又最小范围的错误信息。这样能有效的防治敏感信息的泄漏,这个方法需要用到自定义的数据契约,我们用这个数据契约与服务消费者进行交换。当有错误在服务器段抛出时,我们在异常处理的时候对这个数据契约进行有选择性的填充。服务消费端可以在服务端抛出的异常信息中提取这个保存错误信息的数据契约,并根据它做相应的异常处理。我们县来看个反面例子吧:
反例
调用方法divide,并触发除零错误。
异常信息被拦截,我们可以配置ServiceBehavior的属性<serviceDebug includeExceptionDetailInFaults="false"/>开启异常传递。但是异常信息将会全部暴露给了客户端。
完全返回服务端的异常信息
正例
调用方法divide,并触发除零错误。
服务端返回包含异常明细的数据契约,客户端可以提取该类并进行相应的异常处理。
实现方法
Data Contract, Operation Contract
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace Caculator
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public interface ICalculator
{
[OperationContract]
double Add(double num1, double num2);
[OperationContract]
double Subtrace(double num1, double num2);
[OperationContract]
double Multiply(double num1, double num2);
[OperationContract]
[FaultContract(typeof(CalculationException))]
int Divide(int num1, int num2);
[OperationContract]
string GetData(int value);
//[OperationContract]
//CalculationException GetException();
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
[DataContract]
public class CalculationException
{
private string errorCode = string.Empty;
[DataMember]
public string ErrorCode {
get { return errorCode; }
set { errorCode = value; }
}
private string errorMessage = string.Empty;
[DataMember]
public string ErrorMessage {
get { return errorMessage; }
set { errorMessage = value; }
}
}
}
Service
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;
namespace Caculator
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
public class Service1 : ICalculator
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
public double Add(double num1, double num2)
{
return num1 + num2;
}
public double Subtrace(double num1, double num2)
{
return num1 - num2;
}
public double Multiply(double num1, double num2)
{
return num1 * num2;
}
public int Divide(int num1, int num2)
{
int result;
try
{
result = num1 / num2;
}
//catch (Exception ex)
//{
// throw ex;
//}
catch (DivideByZeroException ex)
{
CalculationException calException = new CalculationException();
calException.ErrorCode = "100000000";
calException.ErrorMessage = "Divide zero exception";
FaultException<CalculationException> fault = new FaultException<CalculationException>(calException, "error");
throw fault;
}
return result;
}
//public CalculationException GetException()
//{
// return new CalculationException();
//}
}
}
Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Web;
namespace Client
{
class Program
{
static void Main(string[] args)
{
Calculator.CalculatorClient calClient = new Calculator.CalculatorClient();
try
{
calClient.Divide(1, 0);
}
//catch (Exception ex)
//{
// Console.WriteLine(ex.Message);
//}
catch (FaultException ex)
{
var faultMessage = ex.CreateMessageFault();
Calculator.CalculationException actualException = null;
if (faultMessage.HasDetail)
{
actualException = faultMessage.GetDetail<Calculator.CalculationException>();
Console.WriteLine(string.Format("ErrorCode:{0}\tErrorMessage:{1}", actualException.ErrorCode, actualException.ErrorMessage));
}
}
Console.ReadLine();
}
}
}
源码下载