短編小説 - 単一のコードベースで複数のSOAP APIに接続できる方法が必要ですAPIのWSDLは、XML名前空間がサイトごとに異なる点を除いて、基本的に同じです。Magento 2 SOAP APIからのSOAP応答を非直列化できない - 応答のXML名前空間とサービス参照の間の不一致WSDL
長い話が(すみません、このがたくさんある)である:MagentoのSOAPのAPI(ダウンロードの受注、アップロード製品、在庫レベルなど)へのクライアントとして
私の.NET 4.5アプリケーション機能します。 アプリケーションでMagento WSDLのサービス参照が使用され、Magento 1.xの場合、クライアントをインスタンス化するときに別のエンドポイントURLを渡すだけで、WebアプリケーションのMagento APIに接続できます。
それでMagento 2が登場しました。そこで、私は、Magento 2とインターフェイスできる新しいバージョンのアプリケーションを作りたかったのです。しかし、大きな課題が生じました。
Magento 2 Webサイトの既知のAPIのWSDLへのサービス参照を作成することから始めました(これはMagento 2のように単純ではありませんでしたが、WSDLは要求がOAUTH認証の場合のみ公開されますが、それは別の話です)。同じWebサイトAPIに接続すると、アプリケーションは正常に機能しました。ただし、他のエンドポイントURLを使用してクライアントをインスタンス化すると、すべてのメソッド呼び出しでnull応答オブジェクトが返されるように見えます。ターゲットWebサイトのWSDLからService Referenceを再作成すると、サービス参照が機能し始めます。明らかに私はこれを行うことはできませんし、すべての異なる可能性のあるターゲットのWebサイト用のアプリケーションの新しいバージョンをコンパイル!
リファレンスWSDLと他のWSDLの違いを見て、Fiddlerのリクエストと応答を追跡しました。問題の根本原因と思われるものに気付きました。 Magento 1.xとは異なり、Magento 2 WSDLには、WSDLに由来するWebサイト固有のXML名前空間があります。
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つの属性をこれは、例えば、サービス参照のReference.csの属性クラスに異なる名前空間の値に変換します。
[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)]
私の結論は、クラスがReference.cs内の属性に応答内で使用されるXML名前空間は、まさにそれに該当しない限り、SOAP応答を直列化復元することができないということです。
最初に、実行時にクラス属性値を変更しようとしましたが、これはうまくいきませんでした。
今IClientMessageInspectorを使用して応答をインターセプトし、指定されたXML名前空間をReference.csのものに置き換えようとしています。私のコードは以下の通りですが、正しく置き換えられるようですが、まだ応答オブジェクトはnullです!
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方法は、置換を作るているように見えるので、私は今、次の何をすべきかについて本当にSTUCKです!
例応答:
<?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>
注:私は(Reference.csで与えられる)要求でXML名前空間が一致しない限り、自分のアプリケーションのサービス要求は、500エラー応答になるだろう同様の問題がありましたターゲットサイト。上記のIClientMessageInspectorのBeforeSendRequestメソッドを使用して置換を行うことで、この問題を回避できました。わかりやすくするために、そのコードを残しました。