我是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>
有没有人认识到这种现象及其解决方案?!
我会很高兴得到所有帮助!
答案 0 :(得分:2)
解决方案是不要尝试将.NET Exception对象传递回客户端。这限制了您运行.NET的客户端。
事实上,它限制了您运行客户端,这些客户端了解您可能抛出的所有异常。如果在服务器上添加新的MyNewException
并将其丢回客户端,该怎么办?客户端需要包含该异常的程序集才能对其进行反序列化。
答案 1 :(得分:0)
我认为你对你正在做的事情过于幻想。如果您只是尝试抛出FaultException,只需执行新的FaultException(错误)。如果要抛出自定义故障类型,则必须做更多工作,但不需要任何消息。这是我发现的VB示例:
Public Function DoSomething() As Data()
Try
DoSomething()
Catch ex As Exception
Throw New FaultException(ex.Message)
End Try
End Function
如果你正在抛出一个自定义类型的错误(例如说PermissionDenied等),你需要为它创建一个对象,这需要更多的工作。
你还要小心你要回到这里。向客户端发回堆栈跟踪等大量详细信息可以帮助攻击者试图侵入系统,并且对标准最终用户没有太多用处。您应该在服务器上记录它。