使用 WSO2 ESB 组合两个查询

Combine two queries using WSO2 ESB

我一直在想办法让 WSO2 的 ESB 调用两个不同的 APIs 并将它们的结果组合成一个响应,而 运行 只会带来麻烦。在最基本的情况下,我有两个后端,我正在向它们发出请求,响应如下:

http://example.com/items:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <response xmlns="http://example.com/response">
            <query name="items" xmlns="http://example.com/query">
                <row>
                    <id>1</id>
                    <name>Item 1</name>
                </row>
                <row>
                    <id>2</id>
                    <name>Item 2</name>
                </row>
            </query>
        </response>
    </soapenv:Body>
</soapenv:Envelope>

http://example.com/parts:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <response xmlns="http://example.com/response">
            <query name="parts" xmlns="http://example.com/query">
                <row>
                    <id>1</id>
                    <part>Part 1.1</part>
                </row>
                <row>
                    <id>1</id>
                    <part>Part 1.2</part>
                </row>
                <row>
                    <id>1</id>
                    <part>Part 1.3</part>
                </row>
                <row>
                    <id>2</id>
                    <part>Part 2.1</part>
                </row>
                <row>
                    <id>2</id>
                    <part>Part 2.2</part>
                </row>
            </query>
        </response>
    </soapenv:Body>
</soapenv:Envelope>

我想请求这两个,然后将它们的结果组合起来如下所示:

<items>
    <item>
        <id>1</id>
        <name>Item 1</name>
        <parts>
            <part>
                <id>1</id>
                <name>Part 1.1</name>
            </part>
            <part>
                <id>1</id>
                <name>Part 1.2</name>
            </part>
            <part>
                <id>1</id>
                <name>Part 1.3</name>
            </part>
        </parts>
    </item>
    <item>
        <id>2</id>
        <name>Item 2</name>
        <parts>
            <part>
                <id>2</id>
                <name>Part 2.1</name>
            </part>
            <part>
                <id>2</id>
                <name>Part 2.2</name>
            </part>
        </parts>
    </item>
</items>

基本上,来自两个 API 的每个响应都有一个 row 的列表,每个列表都包含一个 id 元素。对 /items 的调用中的 id 在该响应中是唯一的,并且来自 parts 的响应中的每一行都有一个 id 将其与来自 [= 的行联系起来17=].

我在 ESB 中有以下 API 定义:

<?xml version="1.0" encoding="UTF-8"?>
<api context="/item_list" name="ItemList" xmlns="http://ws.apache.org/ns/synapse">
    <resource methods="POST" uri-template="/">
        <inSequence>
            <header name="Content-Type" scope="transport" value="text/xml; charset=utf-8"/>
            <clone>
                <target>
                    <sequence>
                        <send>
                            <endpoint>
                                <address format="soap11" uri="http://example.com/items"/>
                            </endpoint>
                        </send>
                    </sequence>
                </target>
                <target>
                    <sequence>
                        <send>
                            <endpoint>
                                <address format="soap11" uri="http://example.com/parts"/>
                            </endpoint>
                        </send>
                    </sequence>
                </target>
            </clone>
        </inSequence>
        <outSequence>
            <aggregate>
                <correlateOn expression="//*[name()='response']/*[name()='query']/*[name()='row']/*[name()='id']" />
                <completeCondition>
                    <messageCount max="2" min="2"/>
                </completeCondition>
                <onComplete expression="//*[name()='response']/*[name()='query']/*[name()='row']">
                    <send/>
                </onComplete>
            </aggregate>
        </outSequence>
        <faultSequence/>
    </resource>
</api>

此处的 inSequence 已大大简化,但它确实发送了两个有效查询并返回了预期的响应。此处编写的 outSequence 从不向客户端发送响应或在服务器上记录错误。如果我从 aggregate 中删除 correlateOn 元素,我会从两个 API 调用之一中随机取回一个 row。我 认为 correlateOn 是我想在这里使用的东西,但我无法从 WSO2 或 Apache 找到任何有用的文档,所以我确定我'我使用不当。我的 XPath 背景很薄弱,所以我相信表达式也可以使用一些工作。

我至少在 clone/aggregate 模式的正确轨道上吗?我将如何将这两个查询的结果组合成类似于我的示例的结果?如果我能得到一些接近的东西,我应该能够用 XSLT 完成剩下的工作。

看看这个演示:

具有项目响应的后端 1:

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="items"
       transports="https http"
       startOnLoad="true">
   <target>
      <inSequence>
         <payloadFactory media-type="xml">
            <format>
               <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                                 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                  <soapenv:Body>
                     <response xmlns="http://example.com/response">
                        <query xmlns="http://example.com/query" name="items">
                           <row>
                              <id>1</id>
                              <name>Item 1</name>
                           </row>
                           <row>
                              <id>2</id>
                              <name>Item 2</name>
                           </row>
                        </query>
                     </response>
                  </soapenv:Body>
               </soapenv:Envelope>
            </format>
            <args/>
         </payloadFactory>
         <log level="full"/>
         <loopback/>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
      <faultSequence/>
   </target>
</proxy>

具有部件响应的后端 2:

<?xml version="1.0" encoding="UTF-8"?>
<proxy xmlns="http://ws.apache.org/ns/synapse"
       name="parts"
       transports="https http"
       startOnLoad="true">
   <target>
      <inSequence>
         <payloadFactory media-type="xml">
            <format>
               <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                                 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                  <soapenv:Body>
                     <response xmlns="http://example.com/response">
                        <query xmlns="http://example.com/query" name="parts">
                           <row>
                              <id>1</id>
                              <part>Part 1.1</part>
                           </row>
                           <row>
                              <id>1</id>
                              <part>Part 1.2</part>
                           </row>
                           <row>
                              <id>1</id>
                              <part>Part 1.3</part>
                           </row>
                           <row>
                              <id>2</id>
                              <part>Part 2.1</part>
                           </row>
                           <row>
                              <id>2</id>
                              <part>Part 2.2</part>
                           </row>
                        </query>
                     </response>
                  </soapenv:Body>
               </soapenv:Envelope>
            </format>
            <args/>
         </payloadFactory>
         <log level="full"/>
         <loopback/>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
      <faultSequence/>
   </target>
</proxy>

我的 API 调用后端 1 和后端 2 并使用 xslt 进行转换:

<?xml version="1.0" encoding="UTF-8"?>
<api xmlns="http://ws.apache.org/ns/synapse"
     name="ItemList"
     context="/item_list">
   <resource methods="POST" uri-template="/">
      <inSequence>
         <header name="Action" scope="default" value="urn:mediate"/>
         <call>
            <endpoint>
               <address uri="http://localhost:8283/services/items.itemsHttpSoap11Endpoint"
                        format="soap11"/>
            </endpoint>
         </call>
         <enrich>
            <source type="inline" clone="true">
               <Payloads/>
            </source>
            <target type="property" property="Items"/>
         </enrich>
         <enrich>
            <source clone="true" xpath="$body/*"/>
            <target action="child" xpath="$ctx:Items"/>
         </enrich>
         <payloadFactory media-type="xml">
            <format>
               <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
                                 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                                 xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
                                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                  <soapenv:Body/>
               </soapenv:Envelope>
            </format>
            <args/>
         </payloadFactory>
         <call>
            <endpoint>
               <address uri="http://localhost:8283/services/parts.partsHttpSoap11Endpoint"
                        format="soap11"/>
            </endpoint>
         </call>
         <enrich>
            <source clone="true" xpath="$body/*[name()='response']/*[name()='query']"/>
            <target type="property" property="Parts"/>
         </enrich>
         <enrich>
            <source type="property" clone="true" property="Parts"/>
            <target action="child" xpath="$ctx:Items"/>
         </enrich>
         <enrich>
            <source type="property" property="Items"/>
            <target type="body"/>
         </enrich>   
         <xslt key="transformTwoSourcesToOneResult"/>            
         <loopback/>
      </inSequence>
      <outSequence>
         <send/>
      </outSequence>
      <faultSequence/>
   </resource>
</api>

我的 xslt 转换:

<?xml version="1.0" encoding="UTF-8"?>
<localEntry key="transformTwoSourcesToOneResult" xmlns="http://ws.apache.org/ns/synapse">
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                              xmlns:ns0="http://example.com/query" 
                              xmlns:ns1="http://example.com/response" 
                              xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                              xmlns:exslt="http://exslt.org/common"
                              xmlns:saxon="http://saxon.sf.net/"
                              xmlns:syn="http://ws.apache.org/ns/synapse"
                              exclude-result-prefixes="ns0 ns1 xs">
    <xsl:output method="xml" encoding="UTF-8" indent="yes"/>
    <xsl:template match="/">
    <xsl:variable name="var1_instance_Payloads" select="."/>
        <items>
            <xsl:for-each select="$var1_instance_Payloads/syn:Payloads">
                <xsl:variable name="var2_Payloads" select="."/> 
            <xsl:for-each select="$var2_Payloads/ns1:response/ns0:query/ns0:row">
                <xsl:variable name="var2_row" select="."/>
                <item>
                    <id>
                        <xsl:value-of select="number(string($var2_row/ns0:id))"/>
                    </id>
                    <name>
                        <xsl:value-of select="string($var2_row/ns0:name)"/>
                    </name>
                    <parts>
                        <xsl:for-each select="$var2_Payloads/ns0:query/ns0:row">
                            <xsl:variable name="var4_row" select="."/>
                            <xsl:if test="string((number(string($var2_row/ns0:id)) = number(string($var4_row/ns0:id)))) != 'false'">
                                <part>
                                    <id>
                                        <xsl:value-of select="number(string($var4_row/ns0:id))"/>
                                    </id>
                                    <name>
                                        <xsl:value-of select="string($var4_row/ns0:part)"/>
                                    </name>
                                </part>
                            </xsl:if>
                        </xsl:for-each>
                    </parts>
                </item>
            </xsl:for-each>
            </xsl:for-each>
        </items>
    </xsl:template>
</xsl:stylesheet>

</localEntry>

API 响应:

<items xmlns="http://ws.apache.org/ns/synapse" xmlns:syn="http://ws.apache.org/ns/synapse" xmlns:saxon="http://saxon.sf.net/" xmlns:exslt="http://exslt.org/common">
   <item>
      <id>1</id>
      <name>Item 1</name>
      <parts>
         <part>
            <id>1</id>
            <name>Part 1.1</name>
         </part>
         <part>
            <id>1</id>
            <name>Part 1.2</name>
         </part>
         <part>
            <id>1</id>
            <name>Part 1.3</name>
         </part>
      </parts>
   </item>
   <item>
      <id>2</id>
      <name>Item 2</name>
      <parts>
         <part>
            <id>2</id>
            <name>Part 2.1</name>
         </part>
         <part>
            <id>2</id>
            <name>Part 2.2</name>
         </part>
      </parts>
   </item>
</items>