【问题标题】:WCF FaultException errorWCF 故障异常错误
【发布时间】:2011-04-06 16:43:29
【问题描述】:

我是 WCF 新手,我遇到了从 WCF 服务向客户端抛出异常的问题。我正在使用从网上复制的代码示例。 (我使用的是 VS2010 .NET Framework 4.0)

我创建了一个 ErrorHandler,其中 ProvideFault 方法如下所示:

public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message msg)
    {
        FaultException<Exception> faultException = new FaultException<Exception>(error, error.Message, new FaultCode("Testing."));
        MessageFault messageFault = faultException.CreateMessageFault();
        msg = Message.CreateMessage(version, messageFault, Constants.FaultAction);
    }

故障合约如下所示:

[FaultContract(typeof(Exception), Action=Constants.FaultAction)]

客户端测试代码如下所示:

        private void button1_Click(object sender, EventArgs e)
    {
        HistorianAccessServiceClient cli = new HistorianAccessServiceClient();
        Tables.Batch bt = new Tables.Batch();
        try
        {
            bt = cli.GetBatch(3241);
        }
        catch (FaultException<Exception> ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

我注意到,如果 ProvideFault 方法的错误参数包含内部异常,则在客户端抛出 System.ServiceModel.CommunicationException(!?),内部异常是 System.Net .WebException,该异常的内部异常是 System.IO.IOException,该异常的内部异常是 System.Net.Sockets.SocketException(错误代码 10054)?!?!

(不幸的是,我安装了瑞典语操作系统,这意味着来自调试器的消息是瑞典语。)

异常消息(谷歌翻译)如下所示:

接收到http://localhost:7070/Historian.WebAccess/HistorianAccessService 的 HTTP 响应时出错。可能是服务端点绑定没有使用 http 协议。也可能是由于http请求的上下文已经被服务器中断(可能是因为服务被终止了)。您可以在服务器日志中找到更多信息。

如果我抛出一个异常没有内部异常,该异常完全由客户端处理好吗!?!?!

我的配置文件看起来像这样(服务):

 <system.serviceModel>
<bindings />
<client />
<services>
  <service name="Historian.WebAccess.HistorianAccessService">
    <host>
      <baseAddresses>
        <!--<add baseAddress="http://localhost:8732/Design_Time_Addresses/Historian.WebAccess/HistorianAccessService/"/>-->
        <add baseAddress="http://localhost:7070/Historian.WebAccess/"/>
      </baseAddresses>
    </host>
    <!-- Service Endpoints -->
    <!-- Unless fully qualified, address is relative to base address supplied above -->
    <!--<endpoint address="HistorianAccessService" binding="wsHttpBinding" contract="Historian.WebAccess.IHistorianAccessService">-->
    <endpoint address="HistorianAccessService" binding="wsHttpBinding" contract="Historian.WebAccess.IHistorianAccessService">
      <!-- 
          Upon deployment, the following identity element should be removed or replaced to reflect the 
          identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
          automatically.
      -->
      <identity>
        <dns value="localhost"/>
      </identity>
    </endpoint>
    <!-- Metadata Endpoints -->
    <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
    <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior>
      <!-- To avoid disclosing metadata information, 
      set the value below to false and remove the metadata endpoint above before deployment -->
      <serviceMetadata httpGetEnabled="false"/>
      <serviceThrottling maxConcurrentCalls="16" maxConcurrentInstances="2147483646" maxConcurrentSessions="10"/>

      <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
      <!-- To receive exception details in faults for debugging purposes, 
      set the value below to true.  Set to false before deployment 
      to avoid disclosing exception information -->
      <serviceDebug includeExceptionDetailInFaults="false"/>
    </behavior>
  </serviceBehaviors>
</behaviors>

配置文件(客户端):

<system.serviceModel>
    <bindings>
        <wsHttpBinding>
            <binding name="WSHttpBinding_IHistorianAccessService" closeTimeout="00:10:00"
                openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
                bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                maxBufferPoolSize="104857600" maxReceivedMessageSize="104857600"
                messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                allowCookies="false">
                <readerQuotas maxDepth="104857600" maxStringContentLength="104857600" maxArrayLength="104857600"
                    maxBytesPerRead="104857600" maxNameTableCharCount="104857600" />
                <reliableSession ordered="true" inactivityTimeout="00:10:00"
                    enabled="false" />
                <security mode="Message">
                    <transport clientCredentialType="Windows" proxyCredentialType="None"
                        realm="" />
                    <message clientCredentialType="Windows" negotiateServiceCredential="true"
                        algorithmSuite="Default" />
                </security>
            </binding>
        </wsHttpBinding>
    </bindings>
    <client>                   
      <endpoint address="http://localhost:7070/Historian.WebAccess/HistorianAccessService"
            binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IHistorianAccessService"
            contract="HistorianAccessHost.IHistorianAccessService"
            name="WSHttpBinding_IHistorianAccessService">
            <identity>
                <dns value="localhost" />
            </identity>
        </endpoint>
    </client>

  <behaviors>
    <endpointBehaviors>
      <behavior>
        <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

有没有人认识到这种现象并解决它?!

如果能得到所有帮助,我将不胜感激!

【问题讨论】:

    标签: wcf


    【解决方案1】:

    解决方案是不要尝试将 .NET 异常对象传递回客户端。这将您限制为运行 .NET 的客户端。

    事实上,它限制了你运行的客户端,这些客户端知道你可能抛出的所有异常。如果你在服务器上添加一个新的MyNewException,然后把它扔回客户端呢?客户端需要包含该异常的程序集才能完全反序列化它。

    【讨论】:

    • 不完全是。由于故障是合同的一部分,因此任何体面的代理生成器都可以为其创建工件。
    • @Johann:他的错误包含 .NET 异常对象,no 代理生成器将能够创建。
    【解决方案2】:

    我认为你对你想做的事情太花哨了。如果你只是想抛出 FaultException,只需执行 new FaultException(error)。如果您要抛出自定义错误类型,则必须做更多的工作,但这些消息内容都不是必需的。这是我找到的一个 VB 示例:

     Public Function DoSomething() As Data() 
            Try
                DoSomething()
            Catch ex As Exception
              Throw New FaultException(ex.Message)
            End Try
        End Function
    

    如果您要抛出自定义类型的错误(例如 PermissionDenied 等),您需要为此创建一个对象,这需要更多的工作。

    您还需要小心返回这里的内容。将堆栈跟踪等大量详细信息发送回客户端可以帮助攻击者试图侵入系统,但对标准最终用户来说并没有多大用处。您应该将其记录在服务器上。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-22
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 2020-11-02
      • 2013-01-13
      相关资源
      最近更新 更多