使用Java HttpURLConnection+Jackson发送JSON请求与curl结果不一致
问题描述
使用curl命令发送带JSON请求体的GET查询Elasticsearch的shakespeare索引,可正常返回结果:
$ curl -H "Content-Type: application/json" -XGET '192.168.0.121:9200/shakespeare/_search?pretty' -d ' { "query" : { "match_phrase" : { "text_entry" : "to be or not to be" } } }'
但用Java结合HttpURLConnection和Jackson实现相同逻辑时,返回结果与curl完全不同,且发现query.asText()为空,查询未正确发送到服务器,尝试query.toString()也无效,代码如下:
URL url = new URL("http://192.168.0.121:9200/shakespeare/_search?pretty"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestProperty("accept", "application/json"); connection.setRequestMethod("GET"); ObjectMapper mapper = new ObjectMapper(); ObjectNode text = mapper.createObjectNode(); text.put("text_entry", "to be or not to be"); ObjectNode match = mapper.createObjectNode(); match.set("match_phrase",text); ObjectNode query = mapper.createObjectNode(); query.set("query", match); connection.setDoOutput(true); OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); wr.write(query.asText()); wr.flush(); System.out.println(query); InputStream responseStream = connection.getInputStream(); String result = new BufferedReader(new InputStreamReader(responseStream)) .lines().collect(Collectors.joining("\n")); System.out.println(result);
问题原因
- Jackson序列化方法错误:
ObjectNode.asText()仅用于获取文本类型节点的内容,当前query是一个嵌套的对象节点,调用该方法会直接返回空字符串,导致没有任何查询内容被发送到服务器。即使改用toString(),虽然能得到JSON字符串,但这种方式不是Jackson推荐的序列化方式,可能存在格式或编码问题。 - GET请求携带请求体的兼容性问题:HTTP规范中GET请求不应该携带请求体,虽然Elasticsearch和curl允许这种用法,但
HttpURLConnection对GET请求处理请求体的行为并不规范,容易导致请求体被忽略或处理异常。
解决方法
1. 修正Jackson序列化方式
使用Jackson的ObjectMapper.writeValueAsString()或writeValueAsBytes()方法将ObjectNode序列化为标准的JSON字符串/字节数组,这是序列化对象节点的正确方式。
2. 改用POST请求(推荐)
Elasticsearch的_search端点支持POST请求,这也是官方推荐的查询方式,能彻底避免GET请求体的兼容性问题。
修正后的完整代码
URL url = new URL("http://192.168.0.121:9200/shakespeare/_search?pretty"); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 设置请求头,明确内容类型和接受格式 connection.setRequestProperty("Content-Type", "application/json"); connection.setRequestProperty("Accept", "application/json"); // 改用POST请求 connection.setRequestMethod("POST"); ObjectMapper mapper = new ObjectMapper(); // 构造查询JSON结构 ObjectNode text = mapper.createObjectNode(); text.put("text_entry", "to be or not to be"); ObjectNode matchPhrase = mapper.createObjectNode(); matchPhrase.set("match_phrase", text); ObjectNode queryNode = mapper.createObjectNode(); queryNode.set("query", matchPhrase); // 启用输出流并写入序列化后的JSON字节数组 connection.setDoOutput(true); try (OutputStream os = connection.getOutputStream()) { byte[] input = mapper.writeValueAsBytes(queryNode); os.write(input, 0, input.length); } // 读取响应 try (InputStream responseStream = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(responseStream))) { String result = reader.lines().collect(Collectors.joining("\n")); System.out.println(result); }
额外说明
如果坚持要使用GET请求,除了修正序列化方式外,还需要强制设置Content-Type头确保请求体被识别,但这种方式不符合HTTP规范,存在兼容性风险,仅作参考:
// 其他代码同POST版本,仅修改请求方法和相关配置 connection.setRequestMethod("GET"); connection.setRequestProperty("Content-Type", "application/json"); // 序列化和写入部分同POST版本
内容的提问来源于stack exchange,提问作者ricksant
相关产品推荐
相关产品推荐

