我在使用 WCF 使用旧版 Web 服务时遇到问题。我解决了一个问题,即 Web 服务使用不同的签名和加密密钥这一事实。但是当我进行网络服务调用时,我得到一个版本未匹配响应。当我查看我在 WSE 中制作的信封时,我注意到肥皂的版本与 WCF 制作的不同。
我的 WSE 信封标题如下所示:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/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" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
而 WCF 封装的标头
<s:Envelope xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:s="http://www.w3.org/2003/05/soap-envelope">
我添加了一些代码来尝试更改肥皂信封的名称空间和前缀。
EndpointAddress serviceEndpoint =
new EndpointAddress(new Uri("http://mylegacywebservice.com"));
CustomBinding binding = new CustomBinding();
AsymmetricSecurityBindingElement securityBE =
SecurityBindingElement.CreateMutualCertificateDuplexBindingElement(
MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
// Add a custom IdentityVerifier because the service uses two certificates
// (one for signing and one for encryption) and an endpoint identity that
// contains a single identity claim.
securityBE.LocalClientSettings.IdentityVerifier = new MyIdentityVerifier();
binding.Elements.Add(securityBE);
CompositeDuplexBindingElement compositeDuplex =
new CompositeDuplexBindingElement();
compositeDuplex.ClientBaseAddress = new Uri("http://mylegacywebservice.com");
binding.Elements.Add(compositeDuplex);
binding.Elements.Add(new OneWayBindingElement());
binding.Elements.Add(new HttpTransportBindingElement());
Dictionary<string, string> namespaceToPrefixMapping = new Dictionary<string, string>
{
{ "http://schemas.xmlsoap.org/soap/envelope/", "soap" },
{ "http://schemas.xmlsoap.org/ws/2004/08/addressing", "wsa" },
};
binding = ReplacePrefixMessageEncodingBindingElement.ReplaceEncodingBindingElement(
new WSHttpBinding(SecurityMode.None),
namespaceToPrefixMapping);
下面是prefixreplace.cs:
public class PrefixReplacer
{
const string XmlnsNamespace = "http://www.w3.org/2000/xmlns/";
Dictionary<string, string> namespaceToNewPrefixMapping = new Dictionary<string, string>();
public void AddNamespace(string namespaceUri, string newPrefix)
{
this.namespaceToNewPrefixMapping.Add(namespaceUri, newPrefix);
}
public void ChangePrefixes(XmlDocument doc)
{
XmlElement element = doc.DocumentElement;
XmlElement newElement = ChangePrefixes(doc, element);
doc.LoadXml(newElement.OuterXml);
}
private XmlElement ChangePrefixes(XmlDocument doc, XmlElement element)
{
string newPrefix;
if (this.namespaceToNewPrefixMapping.TryGetValue(element.NamespaceURI, out newPrefix))
{
XmlElement newElement = doc.CreateElement(newPrefix, element.LocalName, element.NamespaceURI);
List<XmlNode> children = new List<XmlNode>(element.ChildNodes.Cast<XmlNode>());
List<XmlAttribute> attributes = new List<XmlAttribute>(element.Attributes.Cast<XmlAttribute>());
foreach (XmlNode child in children)
{
newElement.AppendChild(child);
}
foreach (XmlAttribute attr in attributes)
{
newElement.Attributes.Append(attr);
}
element = newElement;
}
List<XmlAttribute> newAttributes = new List<XmlAttribute>();
bool modified = false;
for (int i = 0; i < element.Attributes.Count; i++)
{
XmlAttribute attr = element.Attributes[i];
if (this.namespaceToNewPrefixMapping.TryGetValue(attr.NamespaceURI, out newPrefix))
{
XmlAttribute newAttr = doc.CreateAttribute(newPrefix, attr.LocalName, attr.NamespaceURI);
newAttr.Value = attr.Value;
newAttributes.Add(newAttr);
modified = true;
}
else if (attr.NamespaceURI == XmlnsNamespace && this.namespaceToNewPrefixMapping.TryGetValue(attr.Value, out newPrefix))
{
XmlAttribute newAttr;
if (newPrefix != "")
{
newAttr = doc.CreateAttribute("xmlns", newPrefix, XmlnsNamespace);
}
else
{
newAttr = doc.CreateAttribute("xmlns");
}
newAttr.Value = attr.Value;
newAttributes.Add(newAttr);
modified = true;
}
else
{
newAttributes.Add(attr);
}
}
if (modified)
{
element.Attributes.RemoveAll();
foreach (var attr in newAttributes)
{
element.Attributes.Append(attr);
}
}
List<KeyValuePair<XmlNode, XmlNode>> toReplace = new List<KeyValuePair<XmlNode, XmlNode>>();
foreach (XmlNode child in element.ChildNodes)
{
XmlElement childElement = child as XmlElement;
if (childElement != null)
{
XmlElement newChildElement = ChangePrefixes(doc, childElement);
if (newChildElement != childElement)
{
toReplace.Add(new KeyValuePair<XmlNode, XmlNode>(childElement, newChildElement));
}
}
}
if (toReplace.Count > 0)
{
for (int i = 0; i < toReplace.Count; i++)
{
element.InsertAfter(toReplace[i].Value, toReplace[i].Key);
element.RemoveChild(toReplace[i].Key);
}
}
return element;
}
}
下面是 ReplacePrefixMessageEncodingBindingElement:
public class ReplacePrefixMessageEncodingBindingElement : MessageEncodingBindingElement
{
MessageEncodingBindingElement inner;
Dictionary<string, string> namespaceToPrefixMapping = new Dictionary<string, string>();
public ReplacePrefixMessageEncodingBindingElement(MessageEncodingBindingElement inner)
{
this.inner = inner;
}
private ReplacePrefixMessageEncodingBindingElement(ReplacePrefixMessageEncodingBindingElement other)
{
this.inner = other.inner;
this.namespaceToPrefixMapping = new Dictionary<string, string>(other.namespaceToPrefixMapping);
}
public void AddNamespaceMapping(string namespaceUri, string newPrefix)
{
this.namespaceToPrefixMapping.Add(namespaceUri, newPrefix);
}
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new ReplacePrefixMessageEncoderFactory(this.inner.CreateMessageEncoderFactory(), this.namespaceToPrefixMapping);
}
public override MessageVersion MessageVersion
{
get { return this.inner.MessageVersion; }
set { this.inner.MessageVersion = value; }
}
public override BindingElement Clone()
{
return new ReplacePrefixMessageEncodingBindingElement(this);
}
public override IChannelListener<TChannel> BuildChannelListener<TChannel>(BindingContext context)
{
context.BindingParameters.Add(this);
return context.BuildInnerChannelListener<TChannel>();
}
public override bool CanBuildChannelListener<TChannel>(BindingContext context)
{
return context.CanBuildInnerChannelListener<TChannel>();
}
public static CustomBinding ReplaceEncodingBindingElement(Binding originalBinding, Dictionary<string, string> namespaceToPrefixMapping)
{
CustomBinding custom = originalBinding as CustomBinding;
if (custom == null)
{
custom = new CustomBinding(originalBinding);
}
for (int i = 0; i < custom.Elements.Count; i++)
{
if (custom.Elements[i] is MessageEncodingBindingElement)
{
ReplacePrefixMessageEncodingBindingElement element = new ReplacePrefixMessageEncodingBindingElement((MessageEncodingBindingElement)custom.Elements[i]);
foreach (var mapping in namespaceToPrefixMapping)
{
element.AddNamespaceMapping(mapping.Key, mapping.Value);
}
custom.Elements[i] = element;
break;
}
}
return custom;
}