我是WCF的新手,我有问题将WCF服务的异常抛给客户端。我正在使用代码示例,这些代码是我从网上复制的。(我正在使用VS2010 .NET Framework4.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)?!
(不幸的是,我安装了一个瑞典操作系统,这意味着调试器的消息是瑞典语。)
异常消息(google翻译)如下所示:
接收对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>有谁认识到这一现象及其解决办法吗?!
如果我能得到所有的帮助,我会很高兴的!
发布于 2011-04-06 16:47:04
解决方案是不尝试将.NET异常对象传回客户端。这将您限制为运行.NET的客户端。
实际上,它仅限于运行客户端,这些客户端知道可能抛出的所有异常。如果您在服务器上添加了一个新的MyNewException并将其抛回给客户端,该怎么办?客户端需要包含该异常的程序集才能将其反序列化。
发布于 2011-04-06 16:59:44
我觉得你太喜欢你想做的事了。如果您只是试图抛出FaultException,只需执行新的FaultException(错误)。如果您要抛出一个自定义错误类型,您必须做更多的工作,但是这些消息都不是必需的。下面是我发现的一个VB示例:
Public Function DoSomething() As Data()
Try
DoSomething()
Catch ex As Exception
Throw New FaultException(ex.Message)
End Try
End Function如果您要抛出一个自定义类型的错误(比如PermissionDenied之类的),您需要为此创建一个对象,这需要更多的工作。
你也要小心你回来的地方。向客户端发送堆栈跟踪之类的详细信息可以帮助攻击者入侵系统,而且对标准的最终用户没有多大用处。您应该将其记录在服务器上。
https://stackoverflow.com/questions/5569966
复制相似问题