Search code examples
c#wcffaultexception

WSE012: The input was not a valid SOAP message because the following information is missing: action


We're trying to access a webservice (which I believe is also coded using WCF, but I have no control over it whatsoever) and I get an exception:

System.ServiceModel.FaultException: WSE012: The input was not a valid SOAP message because the following information is missing: action.
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.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 ClientName.ProjectName.Services.ServiceName.ServiceNameSoap.CallServiceName(
   Request request)
at ClientName.ProjectName.Scheduler.ThirdPartyName.ProcessServiceName.Execute(
   Object state) in ...\ProcessServiceName.cs:line 65

The calling code in ProcessServiceName.Execute reads:

Request serviceRequest = request.BuildServiceRequest();
Result result = client.CallServiceName(serviceRequest);
LogServiceErrors(db, request.ServiceNameRequestId, result.errors);

and the exception is coming from the second of those lines, which calls directly into the WCF code autogenerated by the Service Reference, calling System.ServiceModel.ClientBase<T>. Channel.CallServiceName(request); the method BuildServiceRequest just takes the database object and converts it to the object required by the webservice.

The relevant part of the App.config (this is called within a Windows Service) reads:

<system.serviceModel>
   <bindings>
      <webHttpBinding>
         <binding name="webBinding">
         </binding>
      </webHttpBinding>
      <wsHttpBinding>
         <binding name="ThirdPartySoap" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00"
          sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false"
          hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
          maxReceivedMessageSize="65536" messageEncoding="Text"
          textEncoding="utf-8" useDefaultWebProxy="true">
            <readerQuotas maxDepth="32" maxStringContentLength="8192"
             maxArrayLength="16384" maxBytesPerRead="4096"
             maxNameTableCharCount="16384"/>
            <security mode="Transport">
               <transport clientCredentialType="Basic" proxyCredentialType="None"
                realm="www.serviceprovider.com"/>
               <message clientCredentialType="UserName"
                algorithmSuite="Default"/>
            </security>
         </binding>
      </wsHttpBinding>
   </bindings>
   <client>
      <endpoint name="ServiceNameSoap"
       address="https://www.serviceprovider.com/ServiceName.asmx"
       binding="wsHttpBinding" bindingConfiguration="ThirdPartySoap"
       contract="ServiceName.ServiceNameSoap"/>
   </client>
</system.serviceModel>

Given that all the code that does the actual webservice work is from within WCF, I don't quite understand what the problem is but, presumably, I need something slightly different in my App.config to prevent it.

Having looked on Google, I'm finding nothing of any help. On StackOverflow, this unanswered WCF question from September 2011 is of no help; custom bindings (answer from November 2009 and link about WSE) don't help (and I barely understand them), and both a forum answer from November 2009 and a ColdFusion thing from October 2009 are unrelated. All the other Google hits I'm seeing are duplicates of these links.

Any assistance would be greatly appreciated!


Edit to add more detail

Wiktor Zychla has rightly pointed out that the problem could (theoretically) be in the proxy classes. However, these classes are wholly unedited from the classes generated automatically; for example:

using System.CodeDom.Compiler;
using System.ServiceModel;
using System.ServiceModel.Channels;

namespace ClientName.ProjectName.Services.ServiceName
{
   [GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
   public interface ServiceNameSoapChannel : ServiceNameSoap, IClientChannel {}

   [System.Diagnostics.DebuggerStepThroughAttribute()]
   [GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
   public partial class ServiceNameSoapClient : ClientBase<ServiceNameSoap>,
     ServiceNameSoap
   {
     public ServiceNameSoapClient() { }

     public ServiceNameSoapClient(string endpointConfigurationName) : 
       base(endpointConfigurationName) { }

     public ServiceNameSoapClient(string endpointConfigurationName,
       string remoteAddress) :
       base(endpointConfigurationName, remoteAddress) { }

     public ServiceNameSoapClient(string endpointConfigurationName,
       EndpointAddress remoteAddress) :
       base(endpointConfigurationName, remoteAddress) { }

     public ServiceNameSoapClient(Binding binding,
       EndpointAddress remoteAddress) : base(binding, remoteAddress) { }

     public Result CallServiceName(Request request)
     { return base.Channel.CallServiceName(request); }
   }
}

All the other proxy classes are similarly unedited from the autogenerated code (which I've edited here for whitespace and namespace optimisation, so that it fits better on screen within StackOverflow; the three namespaces I've added using statements for were fully-qualified on all class references, as was the namespace I've redacted to ClientName.ProjectName.Services.ServiceName in these samples.

Having added WCF Message Logging, I get the following in my message log. (This is from a different service call than the code above, but throwing the same exception, partly because this service call is simpler, partly because not all service calls are being logged, for some reason. The only edit I have made is to make the error message legible without horizontal scrolling.)

<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
  <System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
    <EventID>0</EventID>
    <Type>3</Type>
    <SubType Name="Information">0</SubType>
    <Level>8</Level>
    <TimeCreated SystemTime="2013-01-18T12:00:04.7166250Z" />
    <Source Name="System.ServiceModel.MessageLogging" />
    <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
    <Execution ProcessName="ClientName.ProjectName.Scheduler" ProcessID="8600" ThreadID="10" />
    <Channel/>
    <Computer>MachineNameRedacted</Computer>
  </System>
  <ApplicationData>
    <TraceData>
      <DataItem>
        <MessageLogTraceRecord Time="2013-01-18T12:00:04.7166250+00:00" Source="TransportSend" Type="System.ServiceModel.Dispatcher.OperationFormatter+OperationFormatterMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
          <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
            <s:Header>
              <a:Action s:mustUnderstand="1">http://www.serviceprovider.com/RedactedActionName</a:Action>
              <a:MessageID>urn:uuid:395d6394-5f4b-4954-8df0-8fb82d17072a</a:MessageID>
              <a:ReplyTo>
                <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
              </a:ReplyTo>
              <a:To s:mustUnderstand="1">https://www.serviceprovider.com/ServiceName.asmx</a:To>
            </s:Header>
            <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
              <RedactedActionName xmlns="http://www.serviceprovider.com">
                <brandId>2</brandId>
              </RedactedActionName>
            </s:Body>
          </s:Envelope>
        </MessageLogTraceRecord>
      </DataItem>
    </TraceData>
  </ApplicationData>
</E2ETraceEvent>
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
  <System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
    <EventID>0</EventID>
    <Type>3</Type>
    <SubType Name="Information">0</SubType>
    <Level>8</Level>
    <TimeCreated SystemTime="2013-01-18T12:00:04.7635000Z" />
    <Source Name="System.ServiceModel.MessageLogging" />
    <Correlation ActivityID="{00000000-0000-0000-0000-000000000000}" />
    <Execution ProcessName="ClientName.ProjectName.Scheduler" ProcessID="8600" ThreadID="10" />
    <Channel/>
    <Computer>MachineNameRedacted</Computer>
  </System>
  <ApplicationData>
    <TraceData>
      <DataItem>
        <MessageLogTraceRecord Time="2013-01-18T12:00:04.7635000+00:00" Source="TransportReceive" Type="System.ServiceModel.Channels.BufferedMessage" xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace">
          <soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
            <env:Header xmlns:env="http://www.w3.org/2003/05/soap-envelope">
              <wsa:Action>http://schemas.xmlsoap.org/ws/2004/08/addressing/fault</wsa:Action>
              <wsa:MessageID>urn:uuid:b72c6f30-8409-4b55-8c79-056d82f990a5</wsa:MessageID>
              <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
            </env:Header>
            <soap:Body>
              <soap:Fault>
                <soap:Code>
                  <soap:Value>soap:Sender</soap:Value>
                </soap:Code>
                <soap:Reason>
                  <soap:Text xml:lang="en">
                    WSE012: The input was not a valid SOAP message because
                    the following information is missing: action.
                  </soap:Text>
                </soap:Reason>
              </soap:Fault>
            </soap:Body>
          </soap:Envelope>
        </MessageLogTraceRecord>
      </DataItem>
    </TraceData>
  </ApplicationData>

Even more detail: ServiceNameSoap interface

As Wiktor Zychla has requested, here is the ServiceNameSoap interface declaration, from the autogenerated code, again edited only to fit better on screen (hence the using) and to redact the the client-confidential names:

using System.ServiceModel;

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel","4.0.0.0")]
[ServiceContractAttribute(Namespace="https://www.serviceprovider.com/",
    ConfigurationName="ServiceName.ServiceNameSoap")]
public interface ServiceNameSoap
{
    [OperationContractAttribute(
        Action="https://www.serviceprovider.com/ServiceName",
        ReplyAction="*")]
    [XmlSerializerFormatAttribute(SupportFaults=true)]
    [ServiceKnownTypeAttribute(typeof(ClientRequest))]
    [return: MessageParameterAttribute(Name="result")]
    ClientName.ProjectName.Services.ServiceName.Result CallServiceName(
        ClientName.ProjectName.Services.ServiceName.Request request);
}

Solution

  • We have a winner…

    Wiktor's comments led me to look at Interop between WCF and WSE3.0, and try using MessageVersion.Soap12WSAddressingAugust2004 in a CustomBinding by overriding Binding.CreateBindingElements so that the method ClientName.ProjectName.Services. ServiceName.ServiceNameSoapClient.CallServiceName changed from:

    public Result CallServiceName(Request request)
    {
        return base.Channel.CallServiceName(request);
    }
    

    (as in the original question) to:

    using System.ServiceModel.Channels;
    
    public Result CallTestDrive(Request request)
    {
        BindingElementCollection elements
            = base.Endpoint.Binding.CreateBindingElements();
        elements.Find<MessageEncodingBindingElement>().MessageVersion
            = MessageVersion.Soap12WSAddressingAugust2004;
        base.Endpoint.Binding = new CustomBinding(elements);
    
        return base.Channel.CallTestDrive(request);
    }
    

    (code adapted from Modifying the Binding of a Service on Nicholas Allen's WCF blog).

    This would seem to have solved my problem. At last.

    Thank you very much Wiktor and John Saunders; I wouldn't have got to this without your assistance.


    Edit: Another failed attempt below

    Again for completeness's sake, I shall include another avenue I tried that did not work for me. I also tried following the instructions from this answer to the question WCF CustomBinding Configuration, creating a Custom Binding class in my namespace ClientName.ProjectName.ServiceName:

    using System.ServiceModel;
    using System.ServiceModel.Channels;
    
    public class CustomWsHttpBinding : WSHttpBinding
    {
        public override BindingElementCollection CreateBindingElements()
        {
            BindingElementCollection elements = base.CreateBindingElements();
    
            MessageEncodingBindingElement encodingElement
              = elements.Find<MessageEncodingBindingElement>();
            encodingElement.MessageVersion
              = MessageVersion.Soap12WSAddressingAugust2004;
    
            return elements;
        }
    }
    

    and adding it to the config with:

    <system.serviceModel>
        <extensions>
            <bindingExtensions>
                <add name="CustomWsHttpBinding"
                 type="ClientName.ProjectName.ServiceName.CustomWsHttpBinding, ClientName.ProjectName.ServiceName" />
            </bindingExtensions>
        </extensions>
        <bindings>
            <CustomWsHttpBinding>
                <binding name="ServiceNameSoap" closeTimeout="00:01:00"
                 openTimeout="00:01:00" receiveTimeout="00:10:00"
                 sendTimeout="00:01:00" allowCookies="false"
                 bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"
                 maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                 messageEncoding="Text" textEncoding="utf-8"
                 useDefaultWebProxy="true">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192"
                     maxArrayLength="16384" maxBytesPerRead="4096"
                     maxNameTableCharCount="16384"/>
                    <security mode="Transport">
                        <transport clientCredentialType="Basic"
                         proxyCredentialType="None" realm="www.serviceprovider.com"/>
                        <message clientCredentialType="UserName"
                         algorithmSuite="Default"/>
                    </security>
                </binding>
            </CustomWsHttpBinding>
        </bindings>
        <!-- ... -->
    </system.serviceModel>