3

複合メッセージを返す SOAP Web サービスをセットアップしています。
このメッセージの有効な例は次のとおりです。

<dl190Response xmlns="http://pse/">
    <cdhead cisprik="5563167"/>
    <mvts>
        <mvts_S att="a1">
            <x>x1</x>
            <w>w1</w>
        </mvts_S>
        <mvts_S>
            <x>x2</x>
            <w>w2</w>
        </mvts_S>
    </mvts>
</dl190Response>

これはすべて wsdl できちんと定義されています。

<?xml version="1.0" encoding="UTF-8"?>
<definitions
    xmlns="http://schemas.xmlsoap.org/wsdl/"
    xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
    xmlns:tns="http://pse/"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    name="PSE"
    targetNamespace="http://pse/">
    <types>
        <xs:schema xmlns="http://pse/" targetNamespace="http://pse/">
            <xs:complexType name="cdhead_T">
                <xs:attribute name="cisprik" type="xs:long"/>
            </xs:complexType>
            <xs:complexType name="mvts_T">
                <xs:sequence>
                    <xs:element name="mvts_S" type="mvts_S_T" minOccurs="0" maxOccurs="unbounded"/>
                </xs:sequence>
            </xs:complexType>
            <xs:complexType name="mvts_S_T">
                <xs:sequence>
                    <xs:element name="x" type="xs:string"/>
                    <xs:element name="w" type="xs:string"/>
                </xs:sequence>
                <xs:attribute name="att" type="xs:string" use="optional"/>
            </xs:complexType>
        </xs:schema>
    </types>
    <message name="DL190Req">
        <part name="cdhead" type="tns:cdhead_T"/>
    </message>
    <message name="DL190Res">
        <part name="cdhead" type="tns:cdhead_T"/>
        <part name="mvts" type="tns:mvts_T"/>
    </message>
    <portType name="DLPortType">
        <operation name="dl190">
            <input message="tns:DL190Req"/>
            <output message="tns:DL190Res"/>
        </operation>
    </portType>
    <binding name="DLBinding" type="tns:DLPortType">
        <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
        <operation name="dl190">
            <soap:operation soapAction="http://www.testServer.com/test_soap.php#dl190"/>
            <input>
                <soap:body use="literal" namespace="http://pse/"/>
            </input>
            <output>
                <soap:body use="literal" namespace="http://pse/"/>
            </output>
        </operation>
    </binding>
    <service name="PSE">
        <port name="DLPortType" binding="tns:DLBinding">
            <soap:address location="http://www.testServer.com/test_soap.php"/>
        </port>
    </service>
</definitions>

私はサーバー側の test_soap.php を際限なく正しくするために取り組んできましたが、成功しません。XML を返す時点まで正常に動作しているものの一部は次のとおりです。

<?php
    class PSE {
        function dl190 ($arg) {
            //I don't need to extract the input data just now

            mysql_connect('127.0.0.1:3306', 'user', 'password');
            mysql_select_db('myDatabase');

            $xml = new SimpleXMLElement('<dl190Res/>');
            $xml -> addChild('cdhead');
            $mvts = $xml -> addChild('mvts');

            $rows = mysql_query('select * from trx');
            while($data = mysql_fetch_assoc($rows)) {
                $mvts_S = $mvts -> addChild('mvts_S'); 
                foreach($data as $key => $value) {
                    if ($key == 'att') { $mvts_S -> addAttribute($key, $value);}
                    else    {$mvts_S -> addChild($key, $value);}
                }
            };

            $dom = dom_import_simplexml ($xml) -> ownerDocument;

            // now respond to the request and return the XML
        }

    };
    ini_set( "soap.wsdl_cache_enabled", "0");
    $server = new SoapServer ("test.wsdl");
    $server -> setClass ('PSE');
    $server -> setObject (new PSE());
    $server -> handle();    
?>

適切な応答を得るために考えられることはほぼすべて試しましたが、うまくいきませんでした。以前の一部のみを含むメッセージに対して同じことを行うことができました (私の最新の質問と回答を参照してください)。
しかし、ここでは 2 つのメッセージ部分があるため、成功しません。

$xml コンテンツのデバッグは、SOAP サーバーがエンベロープ + ボディにそれをラップさせた後、まさに私が見たいと思っていたものであることを示しています。

実際には、メッセージ部分が 1 つだけの場合とは状況が異なります。最初に XML 宣言を取り除いて、それを返す限り、メッセージ部分から新しい SoapVar を作成できます。ここでは、戻り値が 2 つの部分で構成されているため、同じことはできません。

だから、私は今、次のうちどれをやるべきか疑問に思っています。

  1. 応答メッセージのクラスを宣言し、それを入力して返します
  2. SoapVar および/または SoapParam でいくつかの魔法を実行します (注意してください、私はすでに多くのことを試しました)
  3. 配列と SoapVar を使っていくつかの魔法を実行します (すでに多くのことを試しました)
  4. どういうわけか (どのように?) wsdl に助けを求める
  5. まったく違うもの
  6. SoapServer でこの悪夢全体を終了し、独自の http 応答をゼロから作成します

これについてのすべての助けに感謝しますので、すべての石けんの専門家は、この質問に答えることを躊躇しないでください!

添加

一時的な回避策として、WSDL を編集し、応答メッセージを 1 つの部分だけに変更しました。これにより、予想される 2 つの部分の連結として予想されるメッセージを渡すことができました (または、返された値に対して SoapVar によって定義された構造の WSDL チェックが行われていないため、その他のメッセージも同様です)。

$xml1 = new SimpleXMLElement('<cdhead/>');
$xml1 -> addAttribute ('xmlns', 'http://pse/');
$xml1 -> addAttribute ('cisprik', $newCisprik);

$xml2 = new SimpleXMLElement('<mvts/>');

$rows = mysql_query('select * from trx');
while($data = mysql_fetch_assoc($rows)) {
    $mvts_S = $xml2 -> addChild('mvts_S'); 
    foreach($data as $key => $value) {
        if ($key == 'att') { $mvts_S -> addAttribute($key, $value);}
        else    {$mvts_S -> addChild($key, $value);}
    }
};

$dom1 = dom_import_simplexml ($xml1) -> ownerDocument;
$dom2 = dom_import_simplexml ($xml2) -> ownerDocument;
$part1 = $dom1 -> saveXML($dom1 -> documentElement);
$part2 = $dom2 -> saveXML($dom2 -> documentElement);

$result = new SoapVar ($part1 . $part2, XSD_ANYXML);

これに関する奇妙な点は、もちろん、連結が有効な XML ではなく、周囲のルート要素が欠けていることですが、SoapVar はとにかくそれを解析できます。

SoapVar と SoapParam / SoapServer の詳細な洞察を持っている人は、2 つのメッセージ部分を返すことが可能かどうかを説明できますか?
そして、その方法を説明しますか?
または、代わりに、他の SOAP 設定でそれを行う方法について詳細な情報を提供してください。

4

2 に答える 2

6

最小限のSoapServerを試してセットアップしました。これが、私が行ったことです。

  1. wsdlおよびPHPスクリプトをフォルダーにコピーします。
  2. phpスクリプトを指すようにwsdlロケーション参照を変更します。
  3. WSDLをSoapUIにインポートしました-これを強くお勧めします。無料です!
  4. サービスを呼び出そうとしました。

これが私の呼び出し要求です:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:pse="http://pse/">
   <soapenv:Header/>
   <soapenv:Body>
      <pse:dl190>
         <cdhead cisprik="0"/>
      </pse:dl190>
   </soapenv:Body>
</soapenv:Envelope>

データベースへの呼び出しが原因で最初は機能しませんでしたが、実際には、soapレイヤーで適切に応答する方法のソリューションのみが必要であると理解しています。残りは理解できます。

簡単な解決策は次のとおりです。

<?php
class PSE
{
    public function dl190($arg)
    {
        //var_dump($arg) is:
        //object(stdClass)#3 (1) {
        //   ["cisprik"]=> int(0)
        //}

        $fakeResult = array();

        $fakeResult[0] = new stdClass();
        $fakeResult[0]->cisprik = 23;

        $fakeResult[1] = array();

        $fakeResult[1][0] = new stdClass();
        $fakeResult[1][0]->att = "a1";
        $fakeResult[1][0]->x = "x1";
        $fakeResult[1][0]->w = "w1";

        $fakeResult[1][1] = new stdClass();
        //$fakeResult[1][1]->att = "a1";
        $fakeResult[1][1]->x = "x2";
        $fakeResult[1][1]->w = "w2";

        return $fakeResult;
    }
}

//ini_set("soap.wsdl_cache_enabled", "0");

$server = new SoapServer ("wsdl.xml");
$server->setObject(new PSE());
$server->handle();

PHPは基本的に、リクエスト引数にstdClassと配列の混合物のみを出力することに注意してください(コメントとして取得したものを上部にダンプしました)。これは悲しいことですが、同じレベルで対応し、帰りにXMLを使用して事態を悪化させないことは公正なことだと思います。

このコードに対して上記のリクエストを実行すると、次の石鹸の応答が返されます。

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://pse/">
   <SOAP-ENV:Body>
      <ns1:dl190Response>
         <cdhead cisprik="23"/>
         <mvts>
            <mvts_S att="a1">
               <x>x1</x>
               <w>w1</w>
            </mvts_S>
            <mvts_S>
               <x>x2</x>
               <w>w2</w>
            </mvts_S>
         </mvts>
      </ns1:dl190Response>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

しかし、改善の余地があります。PHP SoapServer(およびSoapClientも)にはクラスマップと呼ばれる機能があり、それを使用することを強くお勧めします。IDEが任意のタイプのPHPDocオートコンプリートをサポートしている場合は、値の適切な設定を処理するほぼすべての場所でIDEを利用できます。

これがクラスマップ定義のある私のバージョンです。クラス名はWSDLのcomplexTypesにちなんで名前を付ける必要がないという事実を強調するために、すべてに「PSE」というプレフィックスを付けていることに注意してください。

<?php
class PSE
{
    public function dl190(PSE_cdhead_T $arg)
    {
        //  var_dump($arg) is:
        //  object(PSE_cdhead_T)#3 (1) {
        //      ["cisprik"]=> int(0)
        //  }

        $fakeResult = array();
        $fakeResult[0] = new PSE_cdhead_T();
        $fakeResult[0]->cisprik = 23;

        $fakeResult[1] = array();

        $fakeResult[1][0] = new PSE_mvts_S_T();
        $fakeResult[1][0]->att = "a1";
        $fakeResult[1][0]->x = "x1";
        $fakeResult[1][0]->w = "w1";

        $fakeResult[1][1] = new PSE_mvts_S_T();
        //$fakeResult[1][1]->att = "a1";
        $fakeResult[1][1]->x = "x2";
        $fakeResult[1][1]->w = "w2";

        return $fakeResult;
    }
}

class PSE_cdhead_T {
    /**
     * @var int
     */
    public $cisprik;
}


class PSE_mvts_S_T {
    /**
     * @var string
     */
    public $att;

    /**
     * @var string
     */
    public $x;

    /**
     * @var string
     */
    public $w;
}

//ini_set("soap.wsdl_cache_enabled", "0");

$classmap = array(
    'cdhead_T' => 'PSE_cdhead_T',
    'mvts_S_T' => 'PSE_mvts_S_T',
);

$serverOptions = array(
    'encoding' => 'utf-8',
    'classmap' => $classmap,
    'features' => SOAP_SINGLE_ELEMENT_ARRAYS,
);

$server = new SoapServer ("wsdl.xml", $serverOptions);

$server->setObject(new PSE());
$server->handle();

残念ながら、1つの厄介な点は解決されていません。応答では、クラスを使用できませんが、どのインデックスパラメータがどのxml結果にマップされているかについてのヒントなしで配列を使用する必要があります。これは本当に悪いことですが、それを変更するには、WSDLを変更する必要があります。

私はWSDLファイルの作成の専門家ではないことを報告するのは不幸です。応答の唯一の要素として複合型を追加しようとしました。2番目のバージョンのダンプを見ると、クラスPSE_cdhead_Tが取得されていることがわかります。これは、要求メッセージの唯一の部分のマップされたcomplexTypeです。

応答メッセージには2つの部分があるため、SoapServerはそれらを配列内に取得する必要があります。可能な名前付き参照はありません。ここに新しいcomplexTypeを追加し、それに応じてマップに次のように新しいクラスを作成することをお勧めします。

class PSE_DL190_Response
{
    /**
     * @var PSE_cdhead_T
     */
    public $cdhead;
    /**
     * @var PSE_mvts_S_T[]
     */
    public $mvts;
}

次に、応答をより簡単に準備できます。

$fakeResult = new PSE_DL190_Response();
$fakeResult->cdhead = new PSE_cdhead_T(); // Set the one cdhead structure
$fakeResult->mvts[] = new PSE_mvts_S_T(); // Add one mvts structure;

これにより、XML応答が変更される可能性がありますが、影響を評価することはできません。

最後に、PHP用のWSDLコードジェネレーターがいくつかあります。これを試してみてください。クラスマップに必要なクラスが自動的に生成されます。前回それらを試したとき、それらは機能しているように見えましたが、テストしたすべてのWSDLファイルでは機能しませんでした。Soapの定義は複雑すぎて、これを正しく行うことができないようです。しかし、それが機能する場合は、手動で作成するのではなく、それだけの価値があります。

于 2012-10-14T18:25:56.933 に答える
1

私は SOAP の専門家ではありませんが、いくつかのプロジェクトで SOAP を使用してサード パーティ サーバーとやり取りしなければならなかったことは悪夢でした。これは、サーバーの実装があまり良くなく、初心者としての私自身の無知が原因の 1 つです。しかし、 PHP SOAPクラスをそのまま使用しようとすると多くの問題が発生したことを覚えています。その後、 NuSOAP ツールキットを使用するように切り替えました。作業がはるかに簡単になり、私が抱えていた奇妙な問題の多くが解決されました。したがって、NuSOAP のようなツールキットを使用して、物事がより理にかなっているかどうかを確認することをお勧めします。

SOAP は古い仕様であり、それは悪くありませんが、もう作業されていないと思います ( WG は 2009-07-10 で閉鎖されました)。Microsoft SOAP ツールキットは廃止され、廃止されました。そうですね、別のルートに行けるなら、そうします。

多分RESTfulルートに行くように。

REST は、異なるサービス間の疎結合を可能にすることで、Web サーバー間のトランザクションを容易にします。REST は、対応する SOAP ほど強く型付けされていません。REST 言語は名詞と動詞の使用に基づいており、読みやすさを重視しています。SOAP とは異なり、REST は XML 解析を必要とせず、サービス プロバイダーとの間のメッセージ ヘッダーも必要としません。これにより、最終的に使用する帯域幅が少なくなります。REST のエラー処理も、SOAP で使用されるものとは異なります。

于 2012-10-09T21:20:20.797 に答える