解析Silverlight调用WCF/Rest异常的解决方法

2017-03-16 10:13:35来源:http://www.linuxsight.com/blog/16563作者:Linux视野人点击

[ServiceContract]


public interface IService1


{


[OperationContract]


string GetData(int value);


}


接着新建一个服务实现类:


public class Service1 : IService1


{


public string GetData(int value)


{


int i = 0;


int j = 5 / i;


return string.Format("You entered: {0}", value);


}


}


在这里让Service1 抛出”divided by zero exception:”


<system.serviceModel>


<behaviors>


<serviceBehaviors>


<behavior name="ServiceBehavior">


<serviceDebug includeExceptionDetailInFaults="true" />


<serviceMetadata httpGetEnabled="true" />


</behavior>


</serviceBehaviors>


</behaviors>


<services>


<service behaviorConfiguration="ServiceBehavior" name="WcfService1.Service1">


</service>


</services>


</system.serviceModel>


在这里注意<serviceDebug includeExceptionDetailInFaults="true" />


在Silverlight 客户端添加服务引用,名称为:ServiceReference1.


在页面上添加一个按钮,按钮的Click事件代码如下:


private void Button_Click(object sender, RoutedEventArgs e)


{


Service1Client client = new ServiceReference1.Service1Client();


client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(client_GetDataCompleted);


client.GetDataAsync(35); //Try GetData


}


void client_GetDataCompleted(object sender, ServiceReference1.GetDataCompletedEventArgs e)


{


}


运行,结果如下:


可以看到实际的异常是“尝试除以0”,但是由于浏览器限制,所有的异常都是NotFound。


在msdn上有两种方法可以解决这个问题,


最简单的就是在App.xaml.cs代码里面使用RegisterPrefix来 使用备用客户端 HTTP 堆栈


public App()


{


bool bRegisterPrefix = WebRequest.RegisterPrefix(http://localhost:9541/,


WebRequestCreator.ClientHttp);


//other Code


}


再次运行代码:


这是SL调用WCF服务如何处理异常的方式,那么调用Rest服务呢?


首先要修改Web.config 节点下的serviceModel以让它支持Rest。


<system.serviceModel>


<behaviors>


<endpointBehaviors>


<behavior name="EndpointBehavior">


<webHttp helpEnabled="true" defaultOutgoingResponseFormat="Json"


faultExceptionEnabled="true" />


</behavior>


</endpointBehaviors>


<serviceBehaviors>


<behavior name="ServiceBehavior">


<serviceDebug includeExceptionDetailInFaults="true" />


<serviceMetadata httpGetEnabled="true" />


</behavior>


</serviceBehaviors>


</behaviors>


<services>


<service behaviorConfiguration="ServiceBehavior" name="WcfService1.Service1">


<endpoint behaviorConfiguration="EndpointBehavior" binding="webHttpBinding"


bindingConfiguration="" name="Rest" contract="WcfService1.IService1" />


</service>


</services>


</system.serviceModel>


在这里要设置webHttp 节点的faultExceptionEnabled=true.并且设置serviceDebug 的includeExceptionDetailInFaults 为true。


OK,服务的Web.config文件已经配置完毕了,接下来要为GetData方法添加WebGet特性修饰了。


public class Service1 : IService1


{


[WebGet()]


public string GetData(int value)


{


int i = 0;


int j = 5 / i;


return string.Format("You entered: {0}", value);


}


}


运行:


地址为:http://localhost:9541/Service1.svc/help



接着输入地址:http://localhost:9541/Service1.svc/GetData?value=3



可以看到得到了异常信息了。


注意:别忘记了添加跨域和授权文件:crossdomain.xml 和 clientaccesspolicy.xml 到网站根目录。


同样,修改SL客户端页面,添加一个Button,button的代码事件为:


private void btnRest_Click(object sender, RoutedEventArgs e)


{


WebClient wc = new WebClient();


wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(


wc_DownloadStringCompleted);


wc.DownloadStringAsync(new Uri("http://localhost:9541/Service1.svc/GetData?value=3"));


}


void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)


{


if (e.Error != null)


{


throw e.Error;


}


}


运行,点击btnRest


可以看到,Rest 调用的结果仍然是NotFound。


提示让我们查看Response属性和Status属性。


就看看Respone属性的ResponseStrem是什么吧。



可以看到errorMessage 就是返回的错误,很明显,我们需要对它反序列化成Exception的对象。


首先尝试使用DataContractSerializer来反序列化为FaultException类



因为我们尝试反序列化为FaultException类,但是XML数据的Element名称为Fault。所以失败,难道是有Fault类 ?可是找了很久也没发现Fault类。


但是在ReadObject方法中发现了一个verifyObjectName的重载。


将代码修改为:


DataContractSerializer serializer = new DataContractSerializer(


typeof(FaultException));


//object deserializerObject = serializer.ReadObject(errorStream);


object deserializerObject = serializer.ReadObject(XmlReader.Create(errorStream),false);


重新运行:



可以发现虽然序列化是成功的,但是序列化后的值全部是错误的。


最后没办法既然有XML的异常数据,那么可以尝试解析xml数据并使用自定义异常。


首先新建SLFaultException 类,继承Exception:代码如下:


public class SLFaultException : Exception


{


public ExceptionDetail Detail { get; set; }


public SLFaultException() { }


public SLFaultException(string message) : base(message) { }


public SLFaultException(string message, ExceptionDetail detail)


: base(message)


{


Detail = detail;


}


}


完整的代码如下:


void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)


{


if (e.Error != null)


{


if (e.Error is WebException)


{


WebResponse errorResponse = ((WebException)e.Error).Response;


Stream errorStream = errorResponse.GetResponseStream();


XElement rootElement = XElement.Load(errorStream);


XElement detailElement = rootElement


.Descendants()


.First(el => el.Name.LocalName == "ExceptionDetail");


DataContractSerializer serializer = new DataContractSerializer(


typeof(ExceptionDetail));


ExceptionDetail exceptionDetail = (ExceptionDetail)serializer.ReadObject(


detailElement.CreateReader(), true);


SLFaultException faultException = new SLFaultException(


exceptionDetail.Message, exceptionDetail);


throw faultException;


}


}


}


虽然序列化为FaultException是失败的,但是xml节点的ExceptionDetail是可以被反序列回来的,当然上面的处理WebException的过程是可以被封装的,读者自己尝试下吧,呵呵。


结果如下图:



最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台