如何使用自定义的 Soap 标头和正文引发 WCF 异常

How to Throw WCF Exception with Customized Soap Header and Body

提问人:Vincent 提问时间:9/16/2019 最后编辑:Vincent 更新时间:9/20/2019 访问量:609

问:

  1. 我知道如何使用 [FaultException] 并在 WCF 中抛出自定义的 [DataContract]
  2. 我知道如何使用 [MessageContract] 返回带有自定义标头和正文标签的 soap 消息

但是,我要问:如何在具有自定义标头和正文的 WCF 中抛出 [FaultException]?因为当我使用 [FaultException] 抛出 [MessageContract] 时,它总是将我的标头和正文标签包装到 body 标签中。

这不是我想要的。大多数 SOAP 客户端不了解 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;
        }

    }

我想要的肥皂消息是


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

WCF 异常 SOAPHEADER SOAPFAULT

评论


答:

1赞 Abraham Qian 9/16/2019 #1

我很困惑为什么你以这种格式定义肥皂消息结构。但据我所知,IErrorHandler 接口能够在捕获通信错误时自定义错误消息。下面是一个示例,用于捕获通信过程中的错误。

主机中的 Hadler 错误终结点 - WCF - 行为

我们可以在 ProvideFault 方法中添加自定义消息标头。请考虑以下代码。

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

结果。
enter image description here

如果有什么我可以帮忙的,请随时告诉我。
更新。

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

为了创建 ActivityID 字段,我们应该在服务器端启用跟踪和消息日志记录。

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

链接。
关于 ActivityID 字段
配置跟踪和消息日志记录
当客户端上的调用传递参数 0 时。我们使用 fiddler 捕获 http 请求。

enter image description here

评论

0赞 Vincent 9/19/2019
谢谢亚伯拉罕·钱。非常接近要求!我需要一个能够抛出这种格式的肥皂的 WCF 应用程序:learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wstep/...
0赞 Abraham Qian 9/20/2019
这可以通过使用使用 wshttpbinding 创建的 WCF 服务来实现。然后我们抛出一个自定义的 Faultexception。请参阅我更新的回复。