这就是事情:
出于跟踪和安全原因,我有一个业务请求,即所有WCF消息都应该有一个特定的标头。
无论如何,我在客户端和服务上都设置了MessageInspector
的实现 - 到目前为止我们控制了两端 - 并且在原型阶段都运行良好。
然而,今天,某些事情变得糟透了,停止了工作。
我从头开始重新制作原型,一切正常。我整个下午都在丢失弹珠。
相关代码如下:
public class DispatchEndpointBehavior : IEndpointBehavior
{
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
var mi = new MessageInspector();
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(mi);
}
// ...
}
public class DispatchMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
var index = request.Headers.FindHeader("name", "");
if (index == -1)
throw new MessageSecurityException("...");
var value = request.Headers.GetHeader<Guid>(index);
// do something with the value
return null;
}
// ...
}
public class ClientEndpointBehavior : IClientEndpointBehavior
{
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
var mi = new ClientSecurityMessageInspector();
clientRuntime.MessageInspectors.Add(mi);
}
// ...
}
public class ClientSecurityMessageInspector : IClientMessageInspector
{
public object BeforeSendRequest(ref Message request,
IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("name", "", Guid.NewGuid()));
return null;
}
// ...
}
以下是服务的配置:
<system.serviceModel>
<services>
<service behaviorConfiguration="Default" name="[Service Name]">
<endpoint
address=""
binding="basicHttpBinding"
behaviorConfiguration="headerBehavior"
contract="[Service Contract]"/>
<endpoint address="mex" ... />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="Default">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="headerBehavior">
<headerBehavior headerName="token" />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="headerBehavior" type="[Implementation Type]" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
类似地,客户端配置是:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="[Service name]" ... >
<readerQuotas ... />
<security mode="None">
<transport
clientCredentialType="None"
proxyCredentialType="None"
realm=""/>
<message
clientCredentialType="UserName"
algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint
address="http://.../MyService.svc"
binding="basicHttpBinding"
bindingConfiguration="Default"
contract="[Service Contract]"
name="[Service Name]"
behaviorConfiguration="headerBehavior" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="headerBehavior">
<headerBehavior
headerName="prosper-security-token"
securityTokenValueService="[Implementation Type]"
securityTokenValueGetterMethodName="[Method Name]" />
</behavior>
</endpointBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="headerBehavior" type="[Implementation Type]" />
</behaviorExtensions>
</extensions>
</system.serviceModel>
已编辑添加
根据要求,异常堆栈跟踪如下:
(System.ServiceModel.FaultException) This message cannot support the operation because it has been copied.
Server stack trace:
at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at ConsoleApplication1.UserManagementService.IUserManagement.CreateUser(String username, String password, String[] systemCodes)
at ConsoleApplication1.UserManagementService.UserManagementClient.CreateUser(String username, String password, String[] systemCodes) in C:\Users\Paulo Santos\Documents\Visual Studio 2008\Projects\PJonDevelopment\ConsoleApplication1\ConsoleApplication1\Service References\UserManagementService\Reference.cs
at ConsoleApplication1.Program.Main(String[] args) in C:\Users\Paulo Santos\Documents\Visual Studio 2008\Projects\PJonDevelopment\ConsoleApplication1\ConsoleApplication1\Program.cs
已编辑添加
正如我在其中一条评论中所说,我发布了一个适用于此的原型项目:ServiceModel.zip
我不能强调原型的作用。我唯一不知道的是为什么我突然开始得到这个奇怪的信息。我不复制邮件,只处理标题,我搜索的任何地方都说阅读邮件是一个很大的禁忌。
我担心有很多要点检查甚至替换 WCF 模型中的整个消息,架构师设计了这样一个对象,即使你看它也很有用。
如果您有机会进行检查,请制作一个可以维持任何想要检查它的人操纵的恐惧的坚实目标。
答案 0 :(得分:7)
使用Greg Sansoms解决方案,但将原始邮件替换为原始邮件的副本:
public object AfterReceiveRequest(ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
MessageBuffer buffer = request.CreateBufferedCopy(MaxMessageSize);
Message requestCopy = buffer.CreateMessage();
var index = requestCopy.Headers.FindHeader("name", "");
if (index == -1)
throw new MessageSecurityException("...");
var value = requestCopy.Headers.GetHeader<Guid>(index);
// do something with the value
//make sure the orignal message is set to a value wich has not been copied nor read
request = buffer.createMessage();
return null;
}
确保通过ref
传递请求对象答案 1 :(得分:1)
这是因为Message对象只能读取一次。尝试使用缓冲区副本:
public object AfterReceiveRequest(ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
MessageBuffer buffer = reply.CreateBufferedCopy(MaxMessageSize);
Message requestCopy = buffer.CreateMessage();
var index = requestCopy.Headers.FindHeader("name", "");
if (index == -1)
throw new MessageSecurityException("...");
var value = requestCopy.Headers.GetHeader<Guid>(index);
// do something with the value
return null;
}
有关详细信息,请查看MSDN。