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
请问能发现该问题的原因吗?
解决方案
问题出在两个核心点:
请求体序列化重复处理
你手动定义了credentials为JSON格式字符串,却又调用JsonOutput.toJson(credentials)对其再次序列化,导致最终发送的是转义后的嵌套JSON(比如原字符串{"name": ""}会变成"{\"name\": \"\"}"),后端无法解析这种格式错误的请求体,因此没有进入处理逻辑。请求路径不匹配
后端路由是/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
相关产品推荐
相关产品推荐

