You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle插件中HttpURLConnection POST请求无响应问题排查

问题描述

我正在开发一个自定义gradle插件,用于触发API的POST请求。Gradle环境下可选的网络工具有限,UrlConnection是最佳选择。我能够正常触发GET API请求,但POST请求无法正常工作。

Gradle插件客户端代码

interface SwapTestPluginExtension {
    val backEndEndpoint: Property<String>
}

class SwapTestPlugin: Plugin<Project> {
    override fun apply(target: Project) {

        val extension = target.extensions.create("swapTest", SwapTestPluginExtension::class.java)
        target.task("prepareTestEnvironment") {
            doLast {
                try {
                    println("[start] ${SwapTestPlugin::class.simpleName}")
                    executePostApiCall(extension)
                } catch (ex: Exception) {
                    println("Something whent wrong in ${SwapTestPlugin::class.simpleName} with exception ${ex}")
                } finally {
                    println("[end] ${SwapTestPlugin::class.simpleName}")
                }
            }
        }
    }

    private fun executePostApiCall(extension: SwapTestPluginExtension) {
        val credentials = "{\"name\": \"\", \"pwd\" : \"\"}"
        val req = URL("${extension.backEndEndpoint.get()}/test/environment").openConnection() as HttpURLConnection
        req.setRequestMethod("POST")
        req.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
        req.setRequestProperty("Accept", "*/*");
        req.setDoOutput(true)
        println("[swap-test-plugin] prepare to call API")
        req.getOutputStream().write(JsonOutput.toJson(credentials).toByteArray(Charsets.UTF_8)/*.getBytes("UTF-8")*/)
        req.outputStream.flush()
        println("[swap-test-plugin] API has been called, going to read results")
        val br = BufferedReader(InputStreamReader(req.inputStream, Charsets.UTF_8.name()))
        val response = StringBuilder()
        var responseLine: String? = null
        while (br.readLine().also { responseLine = it } != null) {
            response.append(responseLine!!.trim { it <= ' ' })
        }
        println("[swap-test-plugin] prepare to parse result")
        val json = JsonSlurper().parseText(response.toString())
        println("[json] ${json}")
    }

}

后端API方法存根

// controllers/test.js
const config = require('config');
const network = require('../utils/network')
const logger = require('../utils/logger')

exports.setEnvironment = async function(req, res) { 
    logger.log("[start] /test/environment call");
    let result
    if (config.isProduction) {
        result = network.getErrorMsg(501, "This method is only supported for non-production mode for test purposes.")
    } else {
        // TODO clean up database if it is a debug mode
        result = network.getMsg(200, JSON.stringify({"msg" : "just a stub"}));
    }
    logger.log(`[end] response ${JSON.stringify(result)}`)
    network.send(req, res, result);
}

// routes/index.js
...
router.post('/api/v1/test/environment', tests.setEnvironment);
...

抓包信息

Wireshark显示POST请求已发送,但服务器无任何活动,请求似乎从未到达。类似的GET请求可以正常工作。

1589    960.149964545   127.0.0.1   127.0.0.1   HTTP/JSON   100 POST /api/v1/test/environment HTTP/1.1 , JavaScript Object Notation (application/json)
1590    960.149967554   127.0.0.1   127.0.0.1   TCP 66  3100 → 50170 [ACK] Seq=1 Ack=262 Win=65280 Len=0 TSval=3277455079 TSecr=3277455079

请问能发现该问题的原因吗?


解决方案

问题出在两个核心点:

  1. 请求体序列化重复处理
    你手动定义了credentials为JSON格式字符串,却又调用JsonOutput.toJson(credentials)对其再次序列化,导致最终发送的是转义后的嵌套JSON(比如原字符串{"name": ""}会变成"{\"name\": \"\"}"),后端无法解析这种格式错误的请求体,因此没有进入处理逻辑。

  2. 请求路径不匹配
    后端路由是/api/v1/test/environment,但你的客户端代码拼接的路径是${extension.backEndEndpoint.get()}/test/environment,缺少了/api/v1前缀,导致请求无法被后端路由匹配。

此外还有两个细节问题需要修正:

  • 代码拼写错误:Something whent wrong → Something went wrong
  • 流操作未手动关闭,存在资源泄漏风险,建议用Kotlin的use块自动管理流资源

修复后的executePostApiCall方法:

private fun executePostApiCall(extension: SwapTestPluginExtension) {
    // 用Map构建对象,让JsonOutput自动生成正确的JSON,避免手动写字符串出错
    val credentials = mapOf("name" to "", "pwd" to "")
    // 补充/api/v1前缀,匹配后端路由
    val req = URL("${extension.backEndEndpoint.get()}/api/v1/test/environment").openConnection() as HttpURLConnection
    req.requestMethod = "POST"
    req.setRequestProperty("Content-Type", "application/json; charset=UTF-8")
    req.setRequestProperty("Accept", "*/*")
    req.doOutput = true

    println("[swap-test-plugin] prepare to call API")
    // use块自动关闭输出流
    req.outputStream.use { outputStream ->
        val jsonBytes = JsonOutput.toJson(credentials).toByteArray(Charsets.UTF_8)
        outputStream.write(jsonBytes)
        outputStream.flush()
    }

    println("[swap-test-plugin] API has been called, going to read results")
    // use块自动关闭输入流和BufferedReader
    val response = req.inputStream.use { inputStream ->
        BufferedReader(InputStreamReader(inputStream, Charsets.UTF_8)).use { reader ->
            reader.readText()
        }
    }

    println("[swap-test-plugin] prepare to parse result")
    val json = JsonSlurper().parseText(response)
    println("[json] ${json}")
}

内容的提问来源于stack exchange,提问作者Gleichmut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 10:51:59