MessageInspector消息:“此消息不支持该操作,因为它已被复制。”

时间:2011-02-01 23:31:49

标签: .net wcf

这就是事情:

出于跟踪和安全原因,我有一个业务请求,即所有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 模型中的整个消息,架构师设计了这样一个对象,即使你看它也很有用。

如果您有机会进行检查,请制作一个可以维持任何想要检查它的人操纵的恐惧的坚实目标。

2 个答案:

答案 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