2017-02-10 7 views
0

短編小説 - 単一のコードベースで複数の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メソッドを使用して置換を行うことで、この問題を回避できました。わかりやすくするために、そのコードを残しました。

答えて

0

私はAfterReceiveReplyメソッドを変更して動作させました。何らかの理由で、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); 
} 
関連する問題