Tecky‘s Blog

你拍一、我拍一,喝着茅台吹牛逼
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

在SilverLight中创建和处理异常

Posted on 2011-01-20 17:51  Tecky Li  阅读(930)  评论(0编辑  收藏  举报

SilverLight4.0已经支持WCF SOAP 错误编程模型,他可已经WCF端未捕获到的异常正确的传递到SilverLight客户端,方便了调试和呈现。在SilverLight早期的版本,当服务抛出异常时,客户端往往得到的都是HTTP 500错误,服务端的详细错误信息并没有返回给客户端,客户端也就无法访问这个错误的信息,更不能展示给最终用户。客户端得到的仅仅是Not Found的错误,错误信息如下:

image

本文参考了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客户端弹出如下错误:

image

 

另外,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