SilverLight4.0已经支持WCF SOAP 错误编程模型,他可已经WCF端未捕获到的异常正确的传递到SilverLight客户端,方便了调试和呈现。在SilverLight早期的版本,当服务抛出异常时,客户端往往得到的都是HTTP 500错误,服务端的详细错误信息并没有返回给客户端,客户端也就无法访问这个错误的信息,更不能展示给最终用户。客户端得到的仅仅是Not Found的错误,错误信息如下:
本文参考了MSDN的文章Creating and Handling Faults in Silverlight,该文讲述了如何实现在客户端捕获到明确清晰的服务器端异常。至于实现的机理请参见原文,此处不再赘述。
当然国内也有好多的博客写了这个问题,但是都没有把步骤写全。
下面描述一下实现和配置的具体步骤:
1、在WCF服务所在项目中增加类SilverlightFaultBehavior.cs,类代码如下:
namespace MyWcfServices
{
public class SilverlightFaultBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
SilverlightFaultMessageInspector inspector = new SilverlightFaultMessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(inspector);
}
public class SilverlightFaultMessageInspector : IDispatchMessageInspector
{
public void BeforeSendReply(ref Message reply, object correlationState)
{
if (reply.IsFault)
{
HttpResponseMessageProperty property = new HttpResponseMessageProperty();
// Here the response code is changed to 200.
property.StatusCode = System.Net.HttpStatusCode.OK;
reply.Properties[HttpResponseMessageProperty.Name] = property;
}
}
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
{
// Do nothing to the incoming message.
return null;
}
}
// The following methods are stubs and not relevant.
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void Validate(ServiceEndpoint endpoint)
{
}
public override System.Type BehaviorType
{
get { return typeof(SilverlightFaultBehavior); }
}
protected override object CreateBehavior()
{
return new SilverlightFaultBehavior();
}
}
}
2、在具体的服务类上增加特性,比如我有一个WCF服务名为Service2.svc,添加特性的代码如下:
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Service2 : IService2
{
public void DoWork()
{
throw new Exception("Customer error!");
}
}
上面的代码中的一个Operation,DoWork方法已经抛出了一个异常,这个以便于我们待会测试是否能够将服务端异常正确传递到客户端。
3、配置WCF所在的项目的web.Config文件。注意,你的WCF不一定是在一个WCF应用程序项目中,也有可能在一个WEB网站中。所以配置的那个web.config文件一定是WCF服务所在的项目。
配置如下(下面是我的完整Config文件的示例):
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> </system.web> <system.serviceModel> <extensions> <behaviorExtensions> <add name="silverlightFaults" type="MyWcfServices.SilverlightFaultBehavior, MyWcfServices, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="silverlightFaults"> <silverlightFaults /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="true" /> </behavior> </serviceBehaviors> </behaviors> <bindings> <customBinding> <binding name="customBinding0"> <binaryMessageEncoding /> <httpTransport /> </binding> </customBinding> </bindings> <services> <service name="SoloWcfServices"> <endpoint address="" binding="customBinding" behaviorConfiguration="silverlightFaults" bindingConfiguration="customBinding0" contract="SoloWcfServices" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> <service name="Service2"> <endpoint address="" binding="customBinding" behaviorConfiguration="silverlightFaults" bindingConfiguration="customBinding0" contract="Service2" /> <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" /> </service> </services> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> <system.webServer> <modules runAllManagedModulesForAllRequests="true"/> </system.webServer> </configuration>
这里有一下几个方面需要注意:
- behaviorExtensions,type中的类型信息只能写在一行商,不能换行,下面的方式是错误的:
<add name=”silverlightFaults”
type=”Microsoft.Silverlight.Samples.SilverlightFaultBehavior,
SilverlightFaultBehavior,
Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=null”/>
- Services节点中可以定义多个,你如果有多个Service,按照上面的示例,依次定义就可以了。
- <serviceDebug includeExceptionDetailInFaults="true" /> 不能漏掉
3、配置SilverLight客户端项目
在APP的构造函数中增加如下代码:
bool registerResult = WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp); bool httpsResult = WebRequest.RegisterPrefix("https://", WebRequestCreator.ClientHttp);
4、SilverLight引用WCF服务,并调用WCF服务,调用时如下:
SoloWcfServiceReference.SoloWcfServicesClient client = new SoloWcfServiceReference.SoloWcfServicesClient();
client.GetDataUsingListCompleted += new EventHandler<SoloWcfServiceReference.GetDataUsingListCompletedEventArgs>(client_GetDataUsingListCompleted);
client.GetDataUsingListAsync();
client_GetDataUsingListCompleted函数定义如下:
void client_GetDataUsingListCompleted(object sender, SoloWcfServiceReference.GetDataUsingListCompletedEventArgs e)
{
if (e.Error == null)
{
// In case of success
}
else if (e.Error is FaultException<ExceptionDetail>)
{
FaultException<ExceptionDetail> fault = e.Error as FaultException<ExceptionDetail>;
// fault.Detail.Type contains the server exception type.
// fault.Detail.Message contains the server exception message.
// fault.Detail.StackTrace contains the server stack trace.
MessageBox.Show(e.Error.ToString());
}
}
下面我们测试一下,SilverLight客户端弹出如下错误:
另外,WCF运行的时候还可能遇到跨域访问的问题,你需要在WCF所在项目中增加一个clientaccesspolicy.xml文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="*"> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy>
SilverLight错误处理的另一个实现,是CodeProject上一个文章,大家可以参考一下:
http://www.codeproject.com/KB/silverlight/SilverlightExceptions.aspx