【问题标题】:Combine two queries using WSO2 ESB使用 WSO2 ESB 组合两个查询
【发布时间】:2017-06-29 10:44:36
【问题描述】:

我一直在试图弄清楚如何让 WSO2 的 ESB 调用两个不同的 API,并将它们的结果组合成一个响应,结果却遇到了麻烦。在最基本的情况下,我有两个后端请求响应如下:

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 的每个响应都有一个 rows 列表,每个列表都包含一个 id 元素。调用ids 中的/items 在该响应中是唯一的,并且来自parts 的响应中的每一行都有一个id,它将它与来自/items 的一行联系起来。

我在 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 背景很弱,所以我确信表达式也可以使用一些工作。

我是否至少在克隆/聚合模式的正确轨道上?我将如何将这两个查询的结果组合成类似于我的示例的内容?如果我能得到一些更接近的东西,我应该可以用 XSLT 完成剩下的工作。

【问题讨论】:

    标签: xpath wso2 wso2esb synapse


    【解决方案1】:

    看看这个演示:

    带有项目响应的后端 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>
    

    【讨论】:

    • 这很完美!感谢您的详细回复;这解释了我所缺少的一切。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-08
    • 1970-01-01
    • 1970-01-01
    • 2019-05-06
    • 1970-01-01
    • 2017-05-03
    相关资源
    最近更新 更多