簡短的故事是 - 我需要一個代碼庫的方式,以便能夠連接到多個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方法進行替換,我成功解決了這個問題。爲了清楚起見,我已將該代碼留下。