.NET中如何将s:Envelope改为soap12:Envelope格式?
解决.NET WCF生成SOAP12信封前缀
s:改为soap12:的问题 服务器仅接受前缀为soap12:的SOAP12信封(如Postman发送的<soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">),但你的WCF代码默认生成的是s:前缀的信封,导致请求报错。以下是修改方案:
核心问题
WCF默认会为SOAP12的命名空间http://www.w3.org/2003/05/soap-envelope分配s前缀,而服务器要求必须使用soap12前缀,因此需要覆盖WCF的默认前缀分配逻辑。
解决方案:自定义消息编码器
通过自定义TextMessageEncodingBindingElement和消息编码器,在消息序列化阶段修改XML前缀:
1. 实现自定义编码器相关类
using System.IO; using System.ServiceModel.Channels; using System.Xml; using System.Text; public class Soap12PrefixTextMessageEncoder : TextMessageEncoder { private readonly TextMessageEncoder _innerEncoder; public Soap12PrefixTextMessageEncoder(TextMessageEncoder innerEncoder) { _innerEncoder = innerEncoder; } public override string ContentType => _innerEncoder.ContentType; public override string MediaType => _innerEncoder.MediaType; public override MessageVersion MessageVersion => _innerEncoder.MessageVersion; public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) { return _innerEncoder.ReadMessage(stream, maxSizeOfHeaders, contentType); } public override void WriteMessage(Message message, Stream stream) { using (var ms = new MemoryStream()) { // 先让默认编码器写入内存流 _innerEncoder.WriteMessage(message, ms); ms.Position = 0; var xmlDoc = new XmlDocument(); xmlDoc.Load(ms); // 修改SOAP信封节点的前缀和命名空间声明 var envelopeNode = xmlDoc.DocumentElement; if (envelopeNode != null && envelopeNode.NamespaceURI == "http://www.w3.org/2003/05/soap-envelope") { envelopeNode.Prefix = "soap12"; // 添加soap12命名空间声明,移除原有的s前缀声明 if (envelopeNode.Attributes["xmlns:soap12"] == null) { envelopeNode.SetAttribute("xmlns:soap12", envelopeNode.NamespaceURI); var sAttr = envelopeNode.Attributes["xmlns:s"]; sAttr?.RemoveAll(); } } // 统一修改所有SOAP命名空间下元素的前缀 foreach (XmlNode node in xmlDoc.SelectNodes("//*[namespace-uri()='http://www.w3.org/2003/05/soap-envelope']")) { node.Prefix = "soap12"; } // 将修改后的XML写入目标流 xmlDoc.Save(stream); } } public override Stream CreateMessageBuffer() { return _innerEncoder.CreateMessageBuffer(); } } public class Soap12PrefixTextMessageEncodingBindingElement : TextMessageEncodingBindingElement { public Soap12PrefixTextMessageEncodingBindingElement() : base() { } public Soap12PrefixTextMessageEncodingBindingElement(MessageVersion version, Encoding encoding) : base(version, encoding) { } public override MessageEncoderFactory CreateMessageEncoderFactory() { var innerFactory = base.CreateMessageEncoderFactory(); return new Soap12PrefixMessageEncoderFactory(innerFactory); } public override BindingElement Clone() { return new Soap12PrefixTextMessageEncodingBindingElement(this.MessageVersion, this.WriteEncoding); } } public class Soap12PrefixMessageEncoderFactory : MessageEncoderFactory { private readonly MessageEncoderFactory _innerFactory; public Soap12PrefixMessageEncoderFactory(MessageEncoderFactory innerFactory) { _innerFactory = innerFactory; } public override MessageEncoder Encoder => new Soap12PrefixTextMessageEncoder((TextMessageEncoder)_innerFactory.Encoder); public override MessageVersion MessageVersion => _innerFactory.MessageVersion; }
2. 修改绑定代码
将原代码中的TextMessageEncodingBindingElement替换为自定义的Soap12PrefixTextMessageEncodingBindingElement:
System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding(); // 使用自定义编码绑定元素 System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new Soap12PrefixTextMessageEncodingBindingElement(); textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.CreateVersion(System.ServiceModel.EnvelopeVersion.Soap12, System.ServiceModel.Channels.AddressingVersion.WSAddressing10); result.Elements.Add(textBindingElement); System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement(); httpsBindingElement.AllowCookies = true; httpsBindingElement.MaxBufferSize = int.MaxValue; httpsBindingElement.MaxReceivedMessageSize = int.MaxValue; result.Elements.Add(httpsBindingElement); return result;
原理说明
自定义编码器会在WCF默认序列化消息后,将消息内容加载到XML文档中,修改SOAP命名空间对应的前缀为soap12,并调整命名空间声明,最终生成符合服务器要求的信封格式,和Postman发送的请求保持一致。
内容的提问来源于stack exchange,提问作者Ainsley Penza
相关产品推荐
相关产品推荐

