Search code examples
xmlpostgroovyhttpbuilder

Groovy httpBuilder POST XML with Basic Authentication


I am trying to send a POST request to a Restful WS with the request originally being xml and so is the response.

I need to have basic authentication sent as well. At first I was having issues with Classes not being defined and thankfully it took 6 jars to resolve that.

Now I keep getting the following: Caught: groovyx.net.http.HttpResponseException: Bad Request

Sounds like it doesn't like the POST request. I have tried different ways including RESTClient, I have tried to delegate the request in its raw xml format by passing a file or as a string var. I don't fully understand the difference between post or request methods in httpBuilder.

If anyone can help point me in what I did wrong would be very thankful

def http = new HTTPBuilder('http://some_IP:some_Port/')
http.auth.basic('userName','password')
http.post(path:'/path/ToServlet')

http.post(POST,XML)
{

  delegate.contentType="text/xml"
  delegate.headers['Content-Type']="text/xml"
  //delegate.post(getClass().getResource("/query.xml"))
 // body = getClass().getResource("/query.xml")
   body = 
   {
      mkp.xmlDeclaration()

        Request{
          Header{
                     Command('Retrieve')
                     EntityIdentifiers
                     {
                       Identifier(Value:'PhoneNumber', Type:'TelephoneNumber')
                      }
                                  EntityName('Subscriber')
                  }
         }
   }
}

Now in case I translated the XML wrong in my request here is the XML version of it:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Provisioning xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Request>
    <Header>
        <Command>Retrieve</Command>
        <EntityIdentifiers>
            <Identifier Value="phoneNumber" Type="TelephoneNumber" />
        </EntityIdentifiers>
        <EntityName>Subscriber</EntityName>
    </Header>
</Request>
</Provisioning>

Solution

  • okay so after sometime I have gathered the solution was in upgrading groovy library to 2.4 and httpClient4.0.1 and httpBuilder 4.x

    That seems to have done the trick as I was originally working with groovy 1.5.5 The other option you have is to user Java and Groovy together Java to establish the connection using URL and HttpURLConnection classes hopefully there are enough examples there. Be mindful that this is not SSL and may need to take extra steps if you want to do this with SSL and https

    import groovy.xml.*
    import groovy.util.XmlSlurper.*
    import groovy.util.slurpersupport.GPathResult.*
    
    
    import groovyx.net.http.HTTPBuilder
    
    import groovyx.net.http.EncoderRegistry
    import java.net.URLEncoder
    import java.net.URLEncoder.*
    import org.apache.http.client.*
    import org.apache.http.client.HttpResponseException
    import org.apache.http.protocol.ResponseConnControl;
    import org.apache.http.conn.ssl.*
    import org.apache.http.conn.ssl.TrustStrategy
    
    import static java.net.URLEncoder.encode
    import static groovyx.net.http.ContentType.*
    import static groovyx.net.http.HTTPBuilder.request
    import static groovyx.net.http.Method.POST
    //import static groovyx.net.http.RESTClient.*
    
    
    def SMSCmirrorServerTor = "http://IP:PORT"
    def SMSCmirrorServerMntrl = "http://IP:PORT"
    
    
    def msisdn = args[0]
    
    //Connect to method HTTP xml URL encoded request, XML response
    def connectSMSC(server, MSISDN)
    {
      // Variables used for the response
      def result = ""
      //Variables used for request
      // one way is to use raw xml
      def rawXML = """
      <Provisioning xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'>
              <Request>
                      <Header>
                              make sure your <xml> is well formatted use xml formatter online if need be
                      </Header>
              </Request>
      </Provisioning>"""
    
        def http = new HTTPBuilder(server)
        http.auth.basic('username','password')
        http.contentType = TEXT
        http.headers = [Accept: 'application/xml', charset: 'UTF-8']
    
        http.request(POST)
        {
          uri.path = 'Servlet or WS/Path'
          requestContentType = URLENC
          body = "provRequest=" + encode(rawXML,"UTF-8")
    
    
          response.success = { resp, xml ->
    
            def xmlParser = new XmlSlurper().parse(xml)
            //Helps if you know in advance the XML response tree structure at this point it's only xmlSlurper doing the work to parse your xml response
            def ResponseStatus = xmlParser.Response.Header.ResponseStatus
            result += "Response Status: " + ResponseStatus.toString() + "\n================\n"
    
            def ResponseHeader = xmlParser.Response.Header.Errors.Error
            ResponseHeader.children().each
            {
              result += "\n${it.name()}: " + it.text().toString() + "\n"
    
            }
    
            def xmlDataRootNode = xmlParser.Response.Data.Subscriber
            xmlDataRootNode.children().each
            {
    
              if (it.name() == 'UserDevices')
              {
                result += "\n" + it.UserDevice.name() + " :" + "\n-----------------\n"
                it.UserDevice.children().each
                {
                  result += it.name() + " :" + it.text().toString() + "\n"
                }
              }
              else
              {
                 result += "\n${it.name()}: " + it.text().toString() + "\n"
              }
    
            }//End of each iterator on childNodes
    
            if (ResponseStatus.text().toString() == "Failure")
            {
              def ErrorCode = resp.status
              result += "Failed to process command. Bad request or wrong input"
              //result += "\nHTTP Server returned error code: " + ErrorCode.toString()
              // Note this error is for bad input server will still return status code 200 meaning o.k. unlike err 401 unathorized or err 500 those are http errors so be mindful of which error handling we're talking about here application layer vs http protocol layer error handling
            }
    
          }//End of response.success closure
        }//End of http.request(POST) method 
    
      result
    }//end of connectSMSC method
    
    
    //the following is only in case server A is down we can try server B 
    def finalResponse=""
    try
    {
      finalResponse += connectSMSC(SMSCmirrorServerTor,msisdn)
    
    }
    
    catch(org.apache.http.conn.HttpHostConnectException e)
    {
    
      finalResponse += "Encountered HTTP Connection Error\n ${e}\n Unable to connect to host ${SMSCmirrorServerTor}\n Trying to connect to mirror server ${SMSCmirrorServerMntrl} instead\n"
      finalResponse += connectSMSC(SMSCmirrorServerMntrl,msisdn)
    }
    catch (groovyx.net.http.HttpResponseException ex)
    {
    
      finalResponse += "Encountered HTTP Connection Error Code: ${ex.statusCode}\n"
      finalResponse += connectSMSC(SMSCmirrorServerMntrl,msisdn)
    }
    /*
    *
    * java.lang.IllegalArgumentException: port out of range:
    
    */
    catch(java.lang.IllegalArgumentException e)
    {
     finalResponse += "Error: " + e
    }
    
    catch(java.io.IOException e)
    {
    
      finalResponse += "IO Error: " + e
      finalResponse += connectSMSC(SMSCmirrorServerMntrl,msisdn)
    }
    catch(java.io.FileNotFoundException e)
    {
    
      finalResponse += "IO Error: " + e
      finalResponse += connectSMSC(SMSCmirrorServerMntrl,msisdn)
    }
    
    
    println finalResponse.toString()