Vincent
Vincent

Reputation: 59

How to Throw WCF Exception with Customized Soap Header and Body

  1. I know how to use [FaultException] and throw Customized [DataContract] in WCF
  2. I know how to return soap message with Customized Headers and Body Tags using [MessageContract]

However, I'm going to ask: How to throw a [FaultException] in WCF with customized Headers and Body? Because when I use [FaultException] to throw [MessageContract], it always wraps my headers and body tags into body tag.

This is not what I want. Most of the SOAP clients do not understand WCF [FaultException].

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
  <s:Body>
    <s:Fault>
      <faultcode>s:Client</faultcode>
      <faultstring xml:lang="en-CH" />
      <detail>
        <CustomizedError xmlns="http://schemas.datacontract.org/2004/07/PlayWcfFault" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <BodyTag1>Hello BodyTag1</BodyTag1>
          <HeaderTag1>Hello HeaderTag1</HeaderTag1>
        </CustomizedError>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

    [MessageContract]
    public class CustomizedError
    {
        [MessageHeader] public string HeaderTag1;
        [MessageBodyMember] public string BodyTag1;
    }

    [ServiceContract]
    public interface IService1
    {

        [OperationContract]
        [FaultContract(typeof(CustomizedError))]
        CustomizedError GetData();
    }

    public class Service1 : IService1
    {
        public CustomizedError GetData()
        {
            CustomizedError fault = new CustomizedError
            {
                HeaderTag1 = "Hello HeaderTag1",
                BodyTag1 = "Hello BodyTag1",
            };
            throw new FaultException<CustomizedError>(fault, "");
            // return fault;
        }

    }

The soap message I want is


<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header />
          <HeaderTag1>Hello HeaderTag1</HeaderTag1>
</s:Header >
  <s:Body>
    <s:Fault>
          <BodyTag1>Hello BodyTag1</BodyTag1>
      </detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

Upvotes: 1

Views: 654

Answers (1)

Abraham Qian
Abraham Qian

Reputation: 7532

I am confused that why you define the soap message structure in that format. But as far as I know, the IErrorHandler interface is able to custom the fault message when capturing the communication error. Here is an example used to capture the errors during communication.

Hadler Error EndPoint in my host - WCF - Behaviour

We are capable of adding the custom message header in the ProvideFault method. Please consider the below code.

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            FaultException faultException = new FaultException(error.Message);
            MessageFault messageFault = faultException.CreateMessageFault();
            fault = Message.CreateMessage(version, messageFault, error.Message);
            MessageHeader mh = MessageHeader.CreateHeader("itemname", "apptest", "34");
            fault.Headers.Add(mh);
        }

Result.
enter image description here

Feel free to let me know if there is anything I can help with.
Updated.

class Program
    {
        static void Main(string[] args)
        {
            Uri uri = new Uri("http://localhost:4386");
            WSHttpBinding binding = new WSHttpBinding();
            binding.Security.Mode = SecurityMode.None;

            using (ServiceHost sh = new ServiceHost(typeof(TestService), uri))
            {
                sh.AddServiceEndpoint(typeof(ITestService), binding, "");
                ServiceMetadataBehavior smb;
                smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
                if (smb == null)
                {
                    smb = new ServiceMetadataBehavior()
                    {
                        HttpGetEnabled = true
                    };

                    sh.Description.Behaviors.Add(smb);

                }
                Binding mexbinding = MetadataExchangeBindings.CreateMexHttpBinding();
                sh.AddServiceEndpoint(typeof(IMetadataExchange), mexbinding, "mex");


                sh.Opened += delegate
                {
                    Console.WriteLine("service is ready");
                };
                sh.Closed += delegate
                {
                    Console.WriteLine("service is closed");
                };
                sh.Open();

                Console.ReadLine();

                sh.Close();
            }
        }
    }
    [ServiceContract]
    public interface ITestService
    {
        [OperationContract]
        int Div(int x, int y);
    }

    [ServiceBehavior]
    public class TestService : ITestService
    {
        public int Div(int x, int y)
        {
            if (y == 0)
            {
                FaultReason reason = new FaultReason("The divisor cannot be Zero");
                FaultCode code = new FaultCode("NotZero", "mynamespace",new FaultCode("mysubcode"));
                throw new FaultException(reason,code);
            }
            return x / y;
        }
}

In order to create ActivityID field, we should enable tracing and message logging on the server-side.

  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel" propagateActivity="true" switchValue="Information, ActivityTracing">
        <listeners>
          <add type="System.Diagnostics.XmlWriterTraceListener" name="xmlLog" initializeData="myLogs.svclog"/>
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="false"/>
    </diagnostics>
  </system.serviceModel>

Link.
About ActivityID field
Configure tracing and message logging
When the invocation on the client-side passes a parameter 0. We capture the http request by using fiddler.
enter image description here

Upvotes: 1

Related Questions