解析Silverlight调用WCF/Rest异常的解决方法
新建rest服务接口:
[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的过程是可以被封装的,读者自己尝试下吧,呵呵。
结果如下图: