Thursday, September 24, 2009

Using HTTPBuilder to call a RESTful web service

Our team is delivering a new RESTful web service and so I figured that I would scratch my Grails itch and create a small application for testing the new web service.   RESTClient looked like it would be a great match and it would be except that I want to display the XML request being passed to the service as well as the XML response returned from the service.   Displaying the XML request is not a problem.  The problem is that RESTClient automatically parses the response data and returns a GPathResult NodeChild object.

After reviewing the documentation for RESTClient and exchanging some emails with the RESTClient developer, he made the suggestion of using HTTPBuilder instead of RESTClient.  The code shown below is the call to HTTPBuilder to call the new service. This topic came up in another posting and you can see an alternative approach here.

Points to notice
  1. The second parameter to HTTPBuilder  is setting the default content type to TEXT.
  2. The request sets the Accept header to 'application/xml'.
  3. HTTPBuilder.RequestConfigDelegate send method sets the content type to XML
  4. Success handler gets response from 'reader.text' and then pretty-prints the result back in to a String for easy display. 


def txnCreate(TransactionSpec txnSpec, String userid, String pswd) {

        def conf = Configuration.get(3)
        if (conf != null) {
           def ppos = new HTTPBuilder( conf.protocol+"//"+conf.server+":"+conf.port, TEXT )
           ppos.headers = [Accept:'application/xml', 'ppos-user':userid, 'ppos-pswd':pswd]
           
           try {
             ppos.request( PUT ) {
                   uri.path = conf.context + conf.url
                   send( XML ) {
                    TransactionSpec (training:txnSpec.training) {
                        delegate.transClassName txnSpec.transClassName
                        delegate.locationNumber txnSpec.locationNumber
                        delegate.currencyCode txnSpec.currencyCode
                        AssociateSpec {
                            delegate.associateNumber txnSpec.associateSpec.associateNumber 
                        }
                        delegate.terminalName txnSpec.terminalName
                        LineItemSpecs {
                            for (item in txnSpec.lineItemSpecs) {
                                LineItemSpec {
                                    sku(item.sku)
                                    quantity(item.quantity)
                                }
                            }
                        }
                        TenderSpecs {
                            for (tender in txnSpec.tenderSpecs) {
                                TenderSpec {
                                    tenderCode(tender.tenderCode)
                                    cardNumber(tender.cardNumber)
                                    expiryDate(tender.expiryDate)
                                    track1Data(tender.track1Data)
                                    track2Data(tender.track2Data)
                                    track3Data(tender.track3Data)
                                    preLockNumber(tender.preLockNumber)
                                    preLockAmount(tender.preLockAmount)
                                    amount(tender.amount)
                                    precision(tender.precision)
                                }
                            }
                        }
                     }   
                   }
                   // success handler
                   response.success = { resp, reader ->
                        // pretty print format the response
                        def stringWriter = new StringWriter()
                        def node = new XmlParser().parseText(reader.text);
                        new XmlNodePrinter(new PrintWriter(stringWriter)).print(node)
                        return stringWriter.toString()
                   }
                   // failure handler
                   response.failure = { resp ->
                      return 'Response status='+resp.status
                   }
             }
           } catch(Exception e) {
               log.error("Caught exception:", e)
               return e.toString()
           }
        }
    }

2 comments:

  1. Nicely done . . . thanks for the reference. I just wanted to mention, though, that the article that mentions the RESTClient v. HTTPBuilder is actually Groovy, RESTClient, HTTPBuilder, and PUTting Zip Files.

    ReplyDelete
  2. Here is another application of HttpBuilder. http://jlorenzen.blogspot.com/2009/01/testing-rest-services-with-groovy.html

    ReplyDelete