Groovy - REST client without using libraries

Groovy supports HTTP out-of-the-box. Let's see how we can consume a REST API using Groovy without any libraries!

I chose an API I think can be quite useful, and requires no API key to get started with: the Yahoo! Weather API.

Here's how to send a HTTP GET request and print the response:

println new URL( "https://query.yahooapis.com/v1/public/yql?q=" +

       URLEncoder.encode(

               "select wind from weather.forecast where woeid in " +

                       "(select woeid from geo.places(1) where text='chicago, il')",

               'UTF-8' ) ).text

Running this:

groovy weather.groovy

<?xml version="1.0" encoding="UTF-8"?>

<query xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" yahoo:count="1" yahoo:created="2015-09-30T21:04:53Z" yahoo:lang="en-US"><results><channel><yweather:wind xmlns:yweather="http://xml.weather.yahoo.com/ns/rss/1.0" chill="61" direction="50" speed="21"/></channel></results></query><!-- total: 43 -->

<!-- pprd1-node603-lh3.manhattan.bf1.yahoo.com -->

We did not specify which format to use, so we got the default format, XML.

Let's ask for JSON by using a different method which allows us to set some headers (you could add a query parameter format=json if you prefer):

def connection = new URL( "https://query.yahooapis.com/v1/public/yql?q=" +

       URLEncoder.encode(

               "select wind from weather.forecast where woeid in " + "(select woeid from geo.places(1) where text='chicago, il')",

               'UTF-8' ) )

       .openConnection() as HttpURLConnection

// set some headers

connection.setRequestProperty( 'User-Agent', 'groovy-2.4.4' )

connection.setRequestProperty( 'Accept', 'application/json' )

// get the response code - automatically sends the request

println connection.responseCode + ": " + connection.inputStream.text

Running:

groovy weather.groovy

200: {"query":{"count":1,"created":"2015-09-30T21:13:11Z","lang":"en-US","results":{"channel":{"wind":{"chill":"61","direction":"40","speed":"23"}}}}}

Great! Now we've got JSON and know how to set headers.

Next, we can let the user enter the location to query, so we can query lots of places without changing the source!

Also, change the query to select item, which contains lots of interesting information about the weather in the selected location.

def location = System.console().readLine( 'Location: ' )

def connection = new URL( "https://query.yahooapis.com/v1/public/yql?q=" +

       URLEncoder.encode(

               "select item " +

                       "from weather.forecast where woeid in " +

                       "(select woeid from geo.places(1) " +

                       "where text='$location')",

               'UTF-8' ) )

       .openConnection() as HttpURLConnection

// set some headers

connection.setRequestProperty( 'User-Agent', 'groovy-2.4.4' )

connection.setRequestProperty( 'Accept', 'application/json' )

// get the response code - automatically sends the request

println connection.responseCode + ": " + connection.inputStream.text

Running:

groovy weather.groovy

Location: Stockholm, Sweden

200: {"query":{"count":1,"created":"2015-09-30T21:19:42Z","lang":"en-US","results":{"channel":{"item":{"title":"Conditions for Stockholm, SE at 10:48 pm CEST","lat":"59.32","long":"18","link":"http://us.rd.yahoo.com/dailynews/rss/weather/Stockholm__SE/*http://weather.yahoo.com/forecast/SWXX0031_f.html","pubDate":"Wed, 30 Sep 2015 10:48 pm CEST","condition":{"code":"3200","date":"Wed, 30 Sep 2015 10:48 pm CEST","temp":"50","text":"Unknown"},"description":"\n<img src=\"http://l.yimg.com/a/i/us/we/52/3200.gif\"/><br />\n<b>Current Conditions:</b><br />\nUnknown, 50 F<BR />\n<BR /><b>Forecast:</b><BR />\nWed - Partly Cloudy. High: 62 Low: 47<br />\nThu - Partly Cloudy. High: 64 Low: 49<br />\nFri - Sunny. High: 66 Low: 45<br />\nSat - Sunny. High: 59 Low: 45<br />\nSun - Mostly Sunny. High: 59 Low: 43<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Stockholm__SE/*http://weather.yahoo.com/forecast/SWXX0031_f.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>\n","forecast":[{"code":"29","date":"30 Sep 2015","day":"Wed","high":"62","low":"47","text":"Partly Cloudy"},{"code":"30","date":"1 Oct 2015","day":"Thu","high":"64","low":"49","text":"Partly Cloudy"},{"code":"32","date":"2 Oct 2015","day":"Fri","high":"66","low":"45","text":"Sunny"},{"code":"32","date":"3 Oct 2015","day":"Sat","high":"59","low":"45","text":"Sunny"},{"code":"34","date":"4 Oct 2015","day":"Sun","high":"59","low":"43","text":"Mostly Sunny"}],"guid":{"isPermaLink":"false","content":"SWXX0031_2015_10_04_7_00_CEST"}}}}}}

JSON is nice, but this is still pretty unreadable... Let's parse the JSON content with Groovy's awesome JsonSlurper, so we can print a nice report.

import groovy.json.JsonSlurper

def location = System.console().readLine( 'Location: ' )

def connection = new URL( "https://query.yahooapis.com/v1/public/yql?q=" +

       URLEncoder.encode(

               "select item " +

                       "from weather.forecast where woeid in " +

                       "(select woeid from geo.places(1) " +

                       "where text='$location')",

               'UTF-8' ) )

       .openConnection() as HttpURLConnection

// set some headers

connection.setRequestProperty( 'User-Agent', 'groovy-2.4.4' )

connection.setRequestProperty( 'Accept', 'application/json' )

if ( connection.responseCode == 200 ) {

   // get the JSON response

   def json = connection.inputStream.withCloseable { inStream ->

       new JsonSlurper().parse( inStream as InputStream )

   }

   // extract some data from the JSON, printing a report

   def item = json.query.results.channel.item

   println item.title

   println "Temperature: ${item.condition?.temp}, Condition: ${item.condition?.text}"

   // show some forecasts

   println "Forecasts:"

   item.forecast.each { f ->

       println " * ${f.date} - Low: ${f.low}, High: ${f.high}, Condition: ${f.text}"

   }

} else {

   println connection.responseCode + ": " + connection.inputStream.text

}

Finally, running this:

groovy weather.groovy

Location: Stockholm, Sweden

Conditions for Stockholm, SE at 11:18 pm CEST

Temperature: 50, Condition: Partly Cloudy

Forecasts:

 * 30 Sep 2015 - Low: 47, High: 62, Conditions: Partly Cloudy

 * 1 Oct 2015 - Low: 49, High: 64, Conditions: Partly Cloudy

 * 2 Oct 2015 - Low: 45, High: 66, Conditions: Sunny

 * 3 Oct 2015 - Low: 45, High: 59, Conditions: Sunny

 * 4 Oct 2015 - Low: 43, High: 59, Conditions: Mostly Sunny

And that's it! Very easy... to see more advanced things you can do with HttpURLConnection, check this StackOverflow answer (it's in Java, but you can use it all from Groovy, of course, without the mess)!

To get temperatures in degrees Centigrades, add the following text at the end of the main query:  and u='c' 

To make SSL connections work (for access using https without checking SSL certificates at all) add this to your Groovy script:

def sc = SSLContext.getInstance("SSL")

def trustAll = [getAcceptedIssuers: {}, checkClientTrusted: { a, b -> }, checkServerTrusted: { a, b -> }]

sc.init(null, [trustAll as X509TrustManager] as TrustManager[], new SecureRandom())

HttpsURLConnection.defaultSSLSocketFactory = sc.socketFactory

Just for reference, here's the full, formatted JSON response we got from the last couple of requests:

{

   "query": {

       "count": 1,

       "created": "2015-09-30T21:19:42Z",

       "lang": "en-US",

       "results": {

           "channel": {

               "item": {

                   "title": "Conditions for Stockholm, SE at 10:48 pm CEST",

                   "lat": "59.32",

                   "long": "18",

                   "link": "http://us.rd.yahoo.com/dailynews/rss/weather/Stockholm__SE/*http://weather.yahoo.com/forecast/SWXX0031_f.html",

                   "pubDate": "Wed, 30 Sep 2015 10:48 pm CEST",

                   "condition": {

                       "code": "3200",

                       "date": "Wed, 30 Sep 2015 10:48 pm CEST",

                       "temp": "50",

                       "text": "Unknown"

                   },

                   "description": "\n<img src=\"http://l.yimg.com/a/i/us/we/52/3200.gif\"/><br />\n<b>Current Conditions:</b><br />\nUnknown, 50 F<BR />\n<BR /><b>Forecast:</b><BR />\nWed - Partly Cloudy. High: 62 Low: 47<br />\nThu - Partly Cloudy. High: 64 Low: 49<br />\nFri - Sunny. High: 66 Low: 45<br />\nSat - Sunny. High: 59 Low: 45<br />\nSun - Mostly Sunny. High: 59 Low: 43<br />\n<br />\n<a href=\"http://us.rd.yahoo.com/dailynews/rss/weather/Stockholm__SE/*http://weather.yahoo.com/forecast/SWXX0031_f.html\">Full Forecast at Yahoo! Weather</a><BR/><BR/>\n(provided by <a href=\"http://www.weather.com\" >The Weather Channel</a>)<br/>\n",

                   "forecast": [

                       {

                           "code": "29",

                           "date": "30 Sep 2015",

                           "day": "Wed",

                           "high": "62",

                           "low": "47",

                           "text": "Partly Cloudy"

                       },

                       {

                           "code": "30",

                           "date": "1 Oct 2015",

                           "day": "Thu",

                           "high": "64",

                           "low": "49",

                           "text": "Partly Cloudy"

                       },

                       {

                           "code": "32",

                           "date": "2 Oct 2015",

                           "day": "Fri",

                           "high": "66",

                           "low": "45",

                           "text": "Sunny"

                       },

                       {

                           "code": "32",

                           "date": "3 Oct 2015",

                           "day": "Sat",

                           "high": "59",

                           "low": "45",

                           "text": "Sunny"

                       },

                       {

                           "code": "34",

                           "date": "4 Oct 2015",

                           "day": "Sun",

                           "high": "59",

                           "low": "43",

                           "text": "Mostly Sunny"

                       }

                   ],

                   "guid": {

                       "isPermaLink": "false",

                       "content": "SWXX0031_2015_10_04_7_00_CEST"

                   }

               }

           }

       }

   }

}