简体   繁体   中英

MessageInspector message: “This message cannot support the operation because it has been copied.”

Here's the thing:

I have a business request that all WCF messages should have a specific header, for tracking and security reasons.

Anyway, I setup an implementation of MessageInspector on both the client and the service -- we control both ends so far -- and all worked well on the prototype phase.

However, today, something went awire and stopped working.

I redid the prototype from scratch and things works just fine. I'm loosing my marbles over it for the entire afternoon.

The relevant code is as follow:

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;
  }
  // ...
}

Here is the configuration for the service:

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

Analogous, the client configuration is:

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

Edited to Add

As requested, the exception stack trace is as follows:

(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

Edited to Add

As told in one of the comments I've posted a prototype project that works here: ServiceModel.zip

I can't stress enough that the prototype works. The only thing I don't know is why I suddenly started getting this weird message. I don't copy the message and only deal with headers, and everywhere I search say that reading the message is a big no-no.

I dread the fact that with so many points to inspect, even replace, the entire message within WCF model, the architects designed with such an object that even if you look at it it goes poof.

If you're giving the opportunity to inspect, make a solid objec that can sustain the hardships of behing manipulated by anyone who wants to inspect it.

Use Greg Sansoms solution but replace the original message with a copy of the original message:

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;

}

be sure to pass the request object by ref

This is because a Message object can only be read once. Try using a buffer copy:

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;
}

For more info, check out MSDN .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM