如何从Jenkins工作流中调用REST API。

45

我想知道如何从(groovy) Jenkins工作流脚本中调用REST API。 我可以执行"sh 'curl -X POST ...'" - 它可以工作,但构建curl命令作为请求很麻烦,而且处理响应变得复杂。我更愿意使用本地的 Groovy HTTP客户端来编写groovy程序 - 我该从哪一个开始? 由于脚本在Jenkins中运行,在将所有所需的依赖项jar复制到Jenkins上的groovy安装之前需要进行复制步骤,因此希望使用轻量级的东西。


你找到了如何将HTTPBuilder安装到Jenkins中吗? - S.Richmond
S. Richmond,按照问题中提到的方法将所有缺失的JAR文件复制到Groovy libs文件夹中是可行的,但这会使得Jenkins服务器的配置变得过于复杂。我想我还是坚持使用curl。 - Assen Kolov
你能告诉我在Jenkins安装中文件夹的位置吗? - S.Richmond
1
我在Docker镜像中运行Jenkins,其中我已经使用sdkman安装了Groovy。 lib文件夹位于var / jenkins_home / .sdkman / candidates / groovy / 2.4.6 / lib。 - Assen Kolov
6个回答

43

没有导入任何包的本地Groovy代码:

// 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());
}

1
我最终使用了这个,因为“HTTP请求”插件在我们的Windows Jenkins实例上开始出现问题。谢谢! - Cole
URL.openConnection() 可能会引入安全漏洞,因此 Jenkins 建议禁止使用它。如果允许使用,这将为其他第三方插件打开此方法调用,并可能使其更加脆弱。 - old-monk
有没有一种方法可以处理响应头? - Kasun Siyambalapitiya
getHeaderFields() - Jim Perris

27

它支持HTTPS吗?如果支持,如何配置证书?@raitisd - Joey Trang
@Van,我认为你不需要在Jenkins端进行任何配置。你只需在URL中使用https即可。而且你调用的资源必须设置了SSL。 - raitisd
您可以通过设置 "ignoreSslErrors: true" 来忽略证书问题。 - foch
@foch 如何使用 ignoreSslErrors: true?语法是什么? - ANIL
2
有没有一种方法可以处理响应头? - Kasun Siyambalapitiya
您的安装需要HTTP请求插件才能正常工作。 - ingyhere

10

我在安装HTTPBuilder库时遇到了问题,所以最终使用更基本的URL类来创建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    
    }       
}

然后我可以使用以下方式执行GET请求:

HttpResponse resp = doGetHttpRequest("http://some.url");

并且可以使用以下方式通过PUT请求发送JSON数据:

HttpResponse resp = this.doPutHttpRequestWithJson("{\"propA\":\"foo\"}", "http://some.url");

谢谢,这可能是正确的方法。我已经为你的答案点赞,并会在有时间测试后接受它。 - Assen Kolov
1
谢谢,这很棒。我需要添加基本身份验证:def auth_token = "user:github_token"; def basic_auth = "Basic ${auth_token.bytes.encodeBase64().toString()}"; connection.setRequestProperty("Authorization", basic_auth); - J.Z.

2
你尝试过使用Groovy的HTTPBuilder类吗? 例如:
@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"

    }
}

3
根据 OP 的要求,您能否详细说明如何将 HTTPBuilder 库安装到 Jenkins 中?它似乎不是默认可用的。 - S.Richmond
1
我已经更新了示例,展示了如何使用Grapes Grab来拉取依赖的HttpBuilder库,无需额外步骤将其包含在Jenkins类路径中。 - pczeus
我终于开始测试了,但不幸的是它在这里出现了错误:https://gist.github.com/strich/38e472eac507bc73e785 - S.Richmond
我和S. Richmond一样遇到了org.apache.ivy的问题。@pczeus,你有解决办法吗? - Cyrille
我自己也遇到了这个问题(Ivy的问题)。我相信当我清除了.grapes缓存并在IntelliJ中重新构建项目时,它已经解决了,但不确定。无论如何,这里有两个链接可能会有所帮助:https://dev59.com/2o3da4cB1Zd3GeqPwCke和https://dev59.com/D14b5IYBdhLWcg3woS68#34814548?noredirect=1#comment64040038_34814548 - pczeus
有人成功使用过这个吗?在@Grab语句中,我遇到了org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed: General error during conversion: Error grabbing Grapes -- [unresolved dependency: org.codehaus.groovy.modules.http-builder#http-builder;0.7: not found]的问题。 - bsky

2

使用基本身份验证头执行GET请求。

def accessToken = "ACCESS_TOKEN".bytes.encodeBase64().toString()
def req = new URL("https://raw.githubusercontent.com/xxxx/something/hosts").openConnection();
req.setRequestProperty("Authorization", "Basic " + accessToken)
def content = req.getInputStream().getText()

1
阻塞主线程进行I/O调用不是一个好主意。
目前推荐的方法是将I/O操作委托给shell步骤。
另一种需要开发的方式是添加新步骤。顺便提一下,有一个倡议添加一组通用步骤以在管道脚本内安全使用,尽管完整的REST客户端需要自己的插件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接