Friday, March 23, 2012

OSB: Hooking up Rest Service with XML Payload in Query String


Using OSB to hook up Rest Service is fairly common. There are quite a few articles online talking about how to do it. However, I ran into some particular issues where trying to hook up a Rest Service in OSB. The problem is this service uses query string with XML payload.  I had to jump through many hoops to get it to work. This post shows how I did it.

Assume the example Rest Service URL is “http://myRestServer:7001/sbconsole “ , this service takes a query string variable "information", the payload looks like
<location><searchFlag>Address</searchFlag><streetNumber>200</streetNumber><streetName>Water St</streetName></location>

Test Rest Service Directly


When I use Firefox poster to test the Rest service, it works fine. I can see Firefox sends up:
“GET /sbconsole?information=%3Clocation%3E%3CsearchFlag%3EAddress%3C%2FsearchFlag%3E%3CstreetNumber%3E200%3C%2FstreetNumber%3E%3CstreetName%3EWater%20St%3C%2FstreetName%3E%3C%2Flocation%3E HTTP/1.1
Host: myrestserver:7001”

Please note that the actual query string is URL encoded. As you will see, this will cause a couple of issues with OSB.

Business Service


Proxy Service
If you pay close attention, you will see the only difference is that Firefox encodes “Water St” as “Water%20St”, whereas OSB encodes it as “Water+St”.

To the best of my knowledge, both are supposed to be “correct”. But our test Rest Service runs on IIS, it appears to me that IIS does not filter “+” inside the actual data element.

I couldn’t make OSB to encode space as “%20”. I suspect it goes to the Java API “URLEncoder”. According to the API (http://download.oracle.com/javase/6/docs/api/java/net/URLEncoder.html):
 The space character " " is converted into a plus sign "+".

So I had to ask the Rest Service developer to filter “+” inside the code. That solves this part of the problem.
As a side note: IIS does filter “+” sign between element tags, just not inside the actual element data. I also suspect there may be a way to tweak IIS to filter “+” sign inside the element data. But I don’t know how if it really can work that way.

In order to pass in the query string to Business service, I need to escape the special characters like below:
With proxy service, when I call the Business service, I need to pass in “query-string” in HTTP header. However, if I pass in the payload directly, the Proxy service will encode the query string first. When the business service gets the string, it becomes garbled up, and the call will fail.
In order to pass in th
<http:query-string>information=&lt;location&gt;&lt;searchFlag&gt;{$body/typ:AddressValidatorRequest/typ:location/typ:searchFlag/text()}&lt;/searchFlag&gt;&lt;streetNumber&gt;{$body/typ:AddressValidatorRequest/typ:location/typ:streetNumber/text()}&lt;/streetNumber&gt;&lt;streetName&gt;{$body/typ:AddressValidatorRequest/typ:location/typ:streetName/text()}&lt;/streetName&gt;&lt;/location&gt;</http:query-string>

Conclusion
If there is an option to choose what method to use for the Rest service, my recommendation is to use POST, instead of query string (GET), especially if the Rest Service is expecting an XML payload.
With POST method, data is passed to the server as-is, no URL encoding is involved.
Source code
I don't know how to add attachment, so i'll just post the snippet of the source here:
    <con:pipeline name="PipelinePairNode1_request" type="request" xmlns:con="http://www.bea.com/wli/sb/pipeline/config">
      <con:stage name="stage1">
        <con:context>
          <con1:varNsDecl prefix="typ" namespace="
http://www.lasvegasnevada.gov/GisAddressValidator/types" xmlns:con1="http://www.bea.com/wli/sb/stages/config"/>
        </con:context>
        <con:actions>
          <con:wsCallout xmlns:con="
http://www.bea.com/wli/sb/stages/transform/config">
            <con1:id xmlns:con1="
http://www.bea.com/wli/sb/stages/config">_ActionId-8515072324608930629--ca66017.12fdc6a1a2e.-7fe8</con1:id>
            <con:service ref="myRestTest/restGetBS" xsi:type="ref:BusinessServiceRef" xmlns:ref="
http://www.bea.com/wli/sb/reference"/>
            <con:request>
              <con:payload wrapped="false">$requestBodyContent</con:payload>
            </con:request>
            <con:response>
              <con:payload wrapped="false">responseBodyContent</con:payload>
            </con:response>
            <con:requestTransform>
              <con:transport-headers copy-all="false">
                <con1:id xmlns:con1="
http://www.bea.com/wli/sb/stages/config">_ActionId-8515072324608930629--ca66017.12fdc6a1a2e.-7fe7</con1:id>
                <con:header-set>outbound-request</con:header-set>
              </con:transport-headers>
              <con:insert varName="outbound">
                <con1:id xmlns:con1="
http://www.bea.com/wli/sb/stages/config">_ActionId-8515072324608930629--ca66017.12fdc6a1a2e.-7fe5</con1:id>
                <con:location>
                  <con:xpathText xmlns:con="
http://www.bea.com/wli/sb/stages/config">./ctx:transport/ctx:request</con:xpathText>
                </con:location>
                <con:where>first-child</con:where>
                <con:expr>
                  <con:xqueryText xmlns:con="
http://www.bea.com/wli/sb/stages/config"><![CDATA[<http:query-string>information=&lt;location&gt;&lt;searchFlag&gt;{$body/typ:AddressValidatorRequest/typ:location/typ:searchFlag/text()}&lt;/searchFlag&gt;&lt;streetNumber&gt;{$body/typ:AddressValidatorRequest/typ:location/typ:streetNumber/text()}&lt;/streetNumber&gt;&lt;streetName&gt;{$body/typ:AddressValidatorRequest/typ:location/typ:streetName/text()}&lt;/streetName&gt;&lt;/location&gt;</http:query-string>]]></con:xqueryText>
                </con:expr>
              </con:insert>
            </con:requestTransform>
            <con:responseTransform>
              <con:replace varName="body" contents-only="true">
                <con1:id xmlns:con1="
http://www.bea.com/wli/sb/stages/config">_ActionId-8515072324608930629--ca66017.12fdc6a1a2e.-7fe4</con1:id>
                <con:location>
                  <con:xpathText xmlns:con="
http://www.bea.com/wli/sb/stages/config">.</con:xpathText>
                </con:location>
                <con:expr>
                  <con:xqueryText xmlns:con="
http://www.bea.com/wli/sb/stages/config">$responseBodyContent</con:xqueryText>
                </con:expr>
              </con:replace>
            </con:responseTransform>
          </con:wsCallout>
        </con:actions>
      </con:stage>
    </con:pipeline>
Tcpdump
In case you wonder how I captured my tcp packet, here is my sample command
/usr/sbin/tcpdump src 10.128.8.108 or dst 10.128.8.108 -nnvvXSs 1514 -w dump.cap

5 comments:

  1. Hi Yaun,

    I have a similar requiremnts wher ein I have to pass xml in the query string. But I tried a lot of options to convert < to < and > to > in OSB - I am using 11.1.1.3

    But this is not happenning. Can you please help me with it.

    Thanks,
    Prasad

    ReplyDelete
  2. couple of things to try (debug):

    1. use firefox poster to post it up, see if it works, if it does, then pay attention to how firefox encodes the payload (the poster actually shows the payload in the popup box)

    2. use tcpdump or wireshark (ethereal) to capture the payload across the wire (network), see how OSB encodes it

    3. you might be able to dump the payload from REST service application side (IIS, apache or the actual app)

    good luck.

    ReplyDelete
  3. Hi Yuan,

    I am able to invoke the service from business service.

    I do this by

    Content-Type: = application/x-www-form-urlencoded

    and

    query-string: = ?lang=en&submitType=xml&xml=01add

    It works.

    But when I create a query string in
    in Proxy service and insert it to outbound variable as shown below the outbound variable.

    Insert [ fn-bea:inlinedX... ] fn-bea:inlinedXML(concat('', $VarAptSvcQueryString1 , ''))
    [ as first child of ] [ ./ctx:transport... ] ./ctx:transport/ctx:request
    in [ outbound ]


    The outbound variable has the tags missing and only the values in between tags gets inserted.


    So I need to replace the < character with escape character '<'.
    How can this be done - I tried replace method it didnt work


    Thanks

    ReplyDelete
  4. Hi Yuan,

    Could you please send me your code at rishu.sharma85@yahoo.com .I have a similar requirement.

    Thanks and Regards,
    Rishu

    ReplyDelete
  5. This is applicable to www.phoneinlookup.com in business transactions since if we would like to find a potential business partner, we can make use of this technology along with whitepages and yellowpages.

    ReplyDelete