简短的故事是 - 我需要一个代码库的方式,以便能够连接到多个SOAP API的每个除了XML Namespace逐个站点之外,API的WSDL本质上是相同的。不能从Magento的2 SOAP API反串行化SOAP响应 - XML命名空间之间的不匹配响应和服务参考WSDL
长的故事(抱歉,有很多的这个):
我的.NET 4.5的应用程序作为客户端的Magento的SOAP API(下载订单,上传产品,库存水平等)。 应用程序使用服务引用来存储Magento WSDL,对于Magento 1.x,这很好 - 应用程序可以在实例化客户端时通过传递不同的端点URL来连接到任何网站的Magento API。
因此,然后Magento 2来了,我想创造一个新的版本的应用程序,可以与它接口。然而,一个重大的挑战出现了。
我开始创建一个服务引用到一个已知的Magento 2网站API的WSDL(这不是直截了当,因为在Magento 2下WSDL只在请求被OAUTH认证但是那是另外一个故事时暴露)。连接到同一个网站API时,应用程序工作正常。但是,当使用任何其他端点URL来实例化客户端时,每个方法调用似乎都会导致一个空响应对象。如果服务引用从目标网站的WSDL重新创建,然后开始工作。很显然,我无法做到这一点,并为每个不同的目标网站编译新版本的应用程序!
我看了看我的参考WSDL和彼此之间的差异,以及跟踪的请求和响应与小提琴手,和我注意到,我认为是问题的根源的东西。与Magento 1.x不同,Magento 2 WSDL具有特定于WSDL来自的网站的XML名称空间。这转化为不同的命名空间中的值在类服务参考的Reference.cs属性,例如:
Magento的1.x的属性(注意通用命名空间值):
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:Magento")]
[System.ServiceModel.ServiceContractAttribute(Namespace="urn:Magento", ConfigurationName="MagentoAPI.Mage_Api_Model_Server_Wsi_HandlerPortType")]
[System.ServiceModel.MessageBodyMemberAttribute(Namespace="urn:Magento", Order=0)]
的Magento 2属性:
[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)]
我的结论是,SOAP响应不能deserialised除非响应中使用的XML命名空间完全对应,在类的Reference.cs属性。
起初,我试图在运行时using various techniques改变类的属性值,但并没有工作。
现在我想用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;
}
只有1在整个响应XML命名空间,就像我说的,我AfterReceiveReply方法似乎是使替代,所以我现在是为下一步做什么难住了!
回应示例:
<?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>
注:我有相匹配的类似的问题,在我的应用程序的服务请求会得到,除非请求XML命名空间(这是由Reference.cs给出)一个500错误响应目标网站。通过使用上述IClientMessageInspector的BeforeSendRequest方法进行替换,我成功解决了这个问题。为了清楚起见,我已将该代码留下。