Wcf 无法从Magento 2 SOAP API反序列化SOAP响应-响应中的XML命名空间与服务引用WSDL不匹配
简而言之,我需要一种方法,使单个代码库能够连接到多个SOAP API,其中每个API的WSDL本质上是相同的,只是XML命名空间因站点而异 说来话长(很抱歉,有很多这样的事情): 我的.NET4.5应用程序充当MagentoSOAPAPI的客户端(下载订单、上传产品、库存水平等)。 该应用程序使用对股票Magento WSDL的服务引用,对于Magento 1.x来说,这很好——应用程序可以连接到任何网站的Magento API,只要在实例化客户端时传递不同的端点URL即可 于是Magento 2出现了,我想制作一个新版本的应用程序,可以与之交互。然而,出现了一个重大挑战 我首先创建了一个对已知Magento 2网站API的WSDL的服务引用(这并不简单,因为在Magento 2下,只有在请求经过OAUTH身份验证的情况下才会公开WSDL,但这是另一回事)。当连接到同一个网站API时,应用程序运行良好。但是,当使用任何其他端点URL实例化客户机时,每个方法调用似乎都会导致一个空响应对象。如果服务引用是从目标网站的WSDL重新创建的,那么它将开始工作。显然,我不能这样做,为每个不同的目标网站编译一个新版本的应用程序 我查看了我的引用WSDL和另一个WSDL之间的差异,并用Fiddler跟踪了请求和响应,我注意到了一些我认为是问题根源的东西。与Magento1.x不同,Magento2WSDL具有特定于WSDL来源的网站的XML名称空间。这将转换为服务引用的Reference.cs中类属性中的不同命名空间值,例如: Magento 1.x属性(请注意通用名称空间值): Magento 2属性:Wcf 无法从Magento 2 SOAP API反序列化SOAP响应-响应中的XML命名空间与服务引用WSDL不匹配,wcf,soap,wsdl,xml-namespaces,magento2,Wcf,Soap,Wsdl,Xml Namespaces,Magento2,简而言之,我需要一种方法,使单个代码库能够连接到多个SOAP API,其中每个API的WSDL本质上是相同的,只是XML命名空间因站点而异 说来话长(很抱歉,有很多这样的事情): 我的.NET4.5应用程序充当MagentoSOAPAPI的客户端(下载订单、上传产品、库存水平等)。 该应用程序使用对股票Magento WSDL的服务引用,对于Magento 1.x来说,这很好——应用程序可以连接到任何网站的Magento API,只要在实例化客户端时传递不同的端点URL即可 于是Magento
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.my-magento-site.net/soap/default?services=salesCreditmemoRepositoryV1")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://www.my-magento-site.net/soap/default?services=salesCreditmemoRepositoryV1", ConfigurationName="MagentoV2SoapApiV1.SalesCreditmemoRepositoryV1.salesCreditmemoRepositoryV1PortType")]
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://www.my-magento-site.net/soap/default?services=salesCreditmemoRepositoryV1", Order=0)]
我的结论是,除非响应中使用的XML名称空间与Reference.cs中的类属性中的名称空间完全对应,否则SOAP响应无法反序列化
最初,我尝试在运行时更改类属性值,但这不起作用
现在,我尝试使用IClientMessageInspector截取响应,并用Reference.cs中的名称空间替换给定的XML名称空间。下面是我的代码,它似乎正确地进行了替换,但响应对象仍然为空
public class CustomInspectorBehavior : IEndpointBehavior
{
private readonly CustomMessageInspector _clientMessageInspector = new CustomMessageInspector();
public string LastRequestXml { get { return _clientMessageInspector.LastRequestXml; } }
public string LastResponseXml { get { return _clientMessageInspector.LastRequestXml; } }
public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) {}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {}
public void Validate(ServiceEndpoint endpoint) {}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(_clientMessageInspector); }
}
public class CustomMessageInspector : IClientMessageInspector
{
public string LastRequestXml { get; private set; }
public string LastResponseXml { get; private set; }
public void AfterReceiveReply(ref Message reply, object correlationState)
{
LastResponseXml = reply.ToString();
var doc = new XmlDocument();
var ms = new MemoryStream();
var writer = XmlWriter.Create(ms);
reply.WriteMessage(writer);
writer.Flush();
ms.Position = 0;
// Do namespace substitution
doc.Load(ms);
doc.DocumentElement.SetAttribute("xmlns:ns1", "http://www.my-reference-address.net/soap/default?services=salesCreditmemoRepositoryV1");
ms.SetLength(0);
writer = XmlWriter.Create(ms);
doc.WriteTo(writer);
writer.Flush();
ms.Position = 0;
var reader = XmlReader.Create(ms);
reply = Message.CreateMessage(reader, int.MaxValue, reply.Version);
}
public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel) { LastRequestXml = request.ToString(); }
}
public static salesCreditmemoRepositoryV1PortTypeClient GetCreditMemosServiceClient(string apiAddress)
{
const string serviceName = "salesCreditmemoRepositoryV1";
var apiClient = new salesCreditmemoRepositoryV1PortTypeClient(GetSoap12Binding(), new EndpointAddress(apiAddress));
var requestInterceptor = new CustomInspectorBehavior();
apiClient.Endpoint.Behaviors.Add(requestInterceptor);
return apiClient;
}
整个响应中只有一个XML名称空间,正如我所说的,我的AfterReceivePly方法似乎正在进行替换,所以我现在真的被困在下一步该做什么
答复示例:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="http://www.my-magento-site.net/soap/default?services=salesCreditmemoRepositoryV1">
<env:Body>
<ns1:salesCreditmemoRepositoryV1GetListResponse>
<result>
<items/>
<searchCriteria>
<filterGroups>
<item>
<filters>
</filters>
</item>
</filterGroups>
</searchCriteria>
<totalCount>0</totalCount>
</result>
</ns1:salesCreditmemoRepositoryV1GetListResponse>
</env:Body>
</env:Envelope>
0
注意:我遇到了一个类似的问题,我的应用程序的服务请求将得到500个错误响应,除非请求中的XML名称空间(由Reference.cs给出)与目标站点匹配。通过使用上述IClientMessageInspector的BeforeSendRequest方法进行替换,我成功地解决了这个问题。为了清晰起见,我省略了该代码。我通过更改AfterReceivePly方法使其正常工作。出于某种原因,使用XmlDocument来帮助创建修改后的回复是可行的
private const string ReplyXmlNameSpacePattern = @"xmlns:ns1=""(.+)\?services=(.+)""";
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Read reply XML
var doc = new XmlDocument();
var ms = new MemoryStream();
var writer = XmlWriter.Create(ms);
reply.WriteMessage(writer);
writer.Flush();
ms.Position = 0;
doc.Load(ms);
// Replace XML namespace in SOAP envelope
var replacementXmlNameSpace = @"xmlns:ns1=""http://www.my-reference-address.net/soap/default?services=$2""";
var newReplyXml = Regex.Replace(doc.OuterXml, ReplyXmlNameSpacePattern, replacementXmlNameSpace, RegexOptions.Compiled);
doc.LoadXml(newReplyXml);
// Write out the modified reply
ms.SetLength(0);
writer = XmlWriter.Create(ms);
doc.WriteTo(writer);
writer.Flush();
ms.Position = 0;
var reader = XmlReader.Create(ms);
reply = Message.CreateMessage(reader, int.MaxValue, reply.Version);
}
private const string ReplyXmlNameSpacePattern = @"xmlns:ns1=""(.+)\?services=(.+)""";
public void AfterReceiveReply(ref Message reply, object correlationState)
{
// Read reply XML
var doc = new XmlDocument();
var ms = new MemoryStream();
var writer = XmlWriter.Create(ms);
reply.WriteMessage(writer);
writer.Flush();
ms.Position = 0;
doc.Load(ms);
// Replace XML namespace in SOAP envelope
var replacementXmlNameSpace = @"xmlns:ns1=""http://www.my-reference-address.net/soap/default?services=$2""";
var newReplyXml = Regex.Replace(doc.OuterXml, ReplyXmlNameSpacePattern, replacementXmlNameSpace, RegexOptions.Compiled);
doc.LoadXml(newReplyXml);
// Write out the modified reply
ms.SetLength(0);
writer = XmlWriter.Create(ms);
doc.WriteTo(writer);
writer.Flush();
ms.Position = 0;
var reader = XmlReader.Create(ms);
reply = Message.CreateMessage(reader, int.MaxValue, reply.Version);
}