31
votes

I wonder how to call REST API from a (groovy) Jenkins workflow script. I can execute "sh 'curl -X POST ...'" - it works, but building the request as a curl command is cumbersome and processing the response gets complicated. I'd prefer a native Groovy HTTP Client to program in groovy - which one should I start with? As the script is run in Jenkins, there is the step of copying all needed dependency jars to the groovy installation on Jenkins, so something light-weight would be appreciated.

5
Did you find out how to install HTTPBuilder into Jenkins? - S.Richmond
S. Richmond, copying all missing jars to the Groovy libs folder, as mentioned in the question, works, but this makes provisioning of a Jenkins server too complicated. I think I am sticking to curl after all. - Assen Kolov
Would you mind pointing out to me where the folder exists within a jenkins installation? - S.Richmond
I run Jenkins in a docker image where I have installed groovy with skdman. The lib folder is var/jenkins_home/.sdkman/candidates/groovy/2.4.6/lib. - Assen Kolov

5 Answers

29
votes

Native Groovy Code without importing any packages:

// GET
def get = new URL("https://httpbin.org/get").openConnection();
def getRC = get.getResponseCode();
println(getRC);
if(getRC.equals(200)) {
    println(get.getInputStream().getText());
}


// POST
def post = new URL("https://httpbin.org/post").openConnection();
def message = '{"message":"this is a message"}'
post.setRequestMethod("POST")
post.setDoOutput(true)
post.setRequestProperty("Content-Type", "application/json")
post.getOutputStream().write(message.getBytes("UTF-8"));
def postRC = post.getResponseCode();
println(postRC);
if(postRC.equals(200)) {
    println(post.getInputStream().getText());
}
19
votes

There is a built in step available that is using Jenkins HTTP Request Plugin to make http requests.

Plugin: https://wiki.jenkins-ci.org/display/JENKINS/HTTP+Request+Plugin

Step documentation: https://jenkins.io/doc/pipeline/steps/http_request/#httprequest-perform-an-http-request-and-return-a-response-object

Example from the plugin github page:

def response = httpRequest "http://httpbin.org/response-headers?param1=${param1}"
println('Status: '+response.status)
println('Response: '+response.content)
10
votes

I had trouble installing the HTTPBuilder library, so I ended up using the more basic URL class to create an HttpUrlConnection.

HttpResponse doGetHttpRequest(String requestUrl){    
    URL url = new URL(requestUrl);    
    HttpURLConnection connection = url.openConnection();    

    connection.setRequestMethod("GET");    

    //get the request    
    connection.connect();    

    //parse the response    
    HttpResponse resp = new HttpResponse(connection);    

    if(resp.isFailure()){    
        error("\nGET from URL: $requestUrl\n  HTTP Status: $resp.statusCode\n  Message: $resp.message\n  Response Body: $resp.body");    
    }    

    this.printDebug("Request (GET):\n  URL: $requestUrl");    
    this.printDebug("Response:\n  HTTP Status: $resp.statusCode\n  Message: $resp.message\n  Response Body: $resp.body");    

    return resp;    
}  

/**    
 * Posts the json content to the given url and ensures a 200 or 201 status on the response.    
 * If a negative status is returned, an error will be raised and the pipeline will fail.    
 */    
HttpResponse doPostHttpRequestWithJson(String json, String requestUrl){    
    return doHttpRequestWithJson(json, requestUrl, "POST");    
}    

/**    
 * Posts the json content to the given url and ensures a 200 or 201 status on the response.    
 * If a negative status is returned, an error will be raised and the pipeline will fail.    
 */    
HttpResponse doPutHttpRequestWithJson(String json, String requestUrl){    
    return doHttpRequestWithJson(json, requestUrl, "PUT");    
}

/**    
 * Post/Put the json content to the given url and ensures a 200 or 201 status on the response.    
 * If a negative status is returned, an error will be raised and the pipeline will fail.    
 * verb - PUT or POST    
 */    
HttpResponse doHttpRequestWithJson(String json, String requestUrl, String verb){    
    URL url = new URL(requestUrl);    
    HttpURLConnection connection = url.openConnection();    

    connection.setRequestMethod(verb);    
    connection.setRequestProperty("Content-Type", "application/json");    
    connection.doOutput = true;    

    //write the payload to the body of the request    
    def writer = new OutputStreamWriter(connection.outputStream);    
    writer.write(json);    
    writer.flush();    
    writer.close();    

    //post the request    
    connection.connect();    

    //parse the response    
    HttpResponse resp = new HttpResponse(connection);    

    if(resp.isFailure()){    
        error("\n$verb to URL: $requestUrl\n    JSON: $json\n    HTTP Status: $resp.statusCode\n    Message: $resp.message\n    Response Body: $resp.body");    
    }    

    this.printDebug("Request ($verb):\n  URL: $requestUrl\n  JSON: $json");    
    this.printDebug("Response:\n  HTTP Status: $resp.statusCode\n  Message: $resp.message\n  Response Body: $resp.body");    

    return resp;    
}  

class HttpResponse {    

    String body;    
    String message;    
    Integer statusCode;    
    boolean failure = false;    

    public HttpResponse(HttpURLConnection connection){    
        this.statusCode = connection.responseCode;    
        this.message = connection.responseMessage;    

        if(statusCode == 200 || statusCode == 201){    
            this.body = connection.content.text;//this would fail the pipeline if there was a 400    
        }else{    
            this.failure = true;    
            this.body = connection.getErrorStream().text;    
        }    

        connection = null; //set connection to null for good measure, since we are done with it    
    }       
}

And then I can do a GET with something like: HttpResponse resp = doGetHttpRequest("http://some.url");

And a PUT with JSON data using something like: HttpResponse resp = this.doPutHttpRequestWithJson("{\"propA\":\"foo\"}", "http://some.url");

2
votes

Have you tried Groovy's HTTPBuilder Class? For example:

@Grapes(
    @Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7.1')
)

import groovyx.net.http.HTTPBuilder
import static groovyx.net.http.ContentType.*
import static groovyx.net.http.Method.*

def http = new HTTPBuilder("http://api.geonames.org/citiesJSON?north=44.1&south=-9.9&east=-22.4&west=55.2&lang=de&username=demo")

http.request(POST, JSON ) { req ->
    body = []
    response.success = { resp, reader ->
        println "$resp.statusLine   Respond rec"

    }
}
1
votes

Blocking the main thread on I/O calls is not a good idea.

Delegating the I/O operation to a shell step is the recommended way currently.

The other way, which requires development, is to add a new step. By the way, there is an initiative to add a common set of steps to be used securely inside the pipeline script, although a full REST client owes its own plugin.