HttpCilent进行Post请求form-data接口,服务方接收不到参数

结论先行

  1. 生成分隔标识boundary
  2. 在HttpPost中设置Header时带上boundary
  3. 创建MultipartEntity时需要设置boundary

实现代码如下

/*** @param url 调用接口的地址* @param paramMap 调用接口传入的方法体参数*/
public static String postDataByFormData(String url, Map<String, Object> paramMap) {HttpPost post = new HttpPost(url);// 必须在post对象的header中设置boundary才能正常进行form-data调用, 此处的BOUNDARY可以用随机生成的UUID代替, 保证post对象和请求体中的boundary值相同final String BOUNDARY = "----WebKitFormBoundary7MA4YWxkTrZu0gW";post.setHeader("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);String result = "";try (CloseableHttpClient httpClient = HttpClients.createDefault()) {MultipartEntityBuilder builder = MultipartEntityBuilder.create()// 必须: 参数体的builder对象需要设置好分界标识, 该值必须与post对象的boundary值相同.setBoundary(BOUNDARY).setContentType(ContentType.create("multipart/form-data", Consts.UTF_8)).setCharset(StandardCharsets.UTF_8);// 设置传输的参数            paramMap.forEach((k, v) ->builder.addTextBody(k, v.toString(), ContentType.create("multipart/form-data", Consts.UTF_8)));// 创建请求实体HttpEntity entity = builder.build();post.setEntity(entity);HttpResponse resp = httpClient.execute(post);if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {// 接收返回信息result = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);}} catch (IOException e) {e.printStackTrace();}return result;
}

背景

之前与其他系统的接口对接都使用application/json格式的请求,使用HttpClient感觉非常简单,这次发现对方使用的contentType是form-data方式的,使用postman轻轻松松调通,结果到java代码中却不好使,对方接口一直返回缺少参数。
Java版本:1.8
HttpClient版本:4.5.6

postman测试结果

在这里插入图片描述

java代码中运行结果

使用HttpClient失败的结果

上述结果的java代码如下

/*** @param url 调用接口的地址* @param paramMap 调用接口传入的方法体参数, 该map对象包含属性 timestamp, params及token校验*/
public static String postDataByFormData(String url, Map<String, Object> paramMap) {HttpPost post = new HttpPost(url);String result = "";try (CloseableHttpClient httpClient = HttpClients.createDefault()) {MultipartEntityBuilder builder = MultipartEntityBuilder.create().setContentType(ContentType.create("multipart/form-data", Consts.UTF_8)).setCharset(StandardCharsets.UTF_8);paramMap.forEach((k, v) -> builder.addTextBody(k, v.toString(), ContentType.create("multipart/form-data", Consts.UTF_8)));// 设置实体HttpEntity entity = builder.build();post.setEntity(entity);HttpResponse resp = httpClient.execute(post);if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {// 返回result = EntityUtils.toString(resp.getEntity(), StandardCharsets.UTF_8);}} catch (IOException e) {e.printStackTrace();}return result;
}

问题处理思路

一开始我认为代码中修改contentType应该就跟postman中一样简单,只需要修改传入的contentType即可,结果失败了,于是我就在某度中搜索使用HttpClient调用form-data的写法,结果都不行,例如:

  1. 试试设置Mode为HttpMultipartMode.BROWSER_COMPATIBLE
MultipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
  1. 添加参数时设置文本参数为text/plain
builder.addTextBody(key, ContentType.DEFAULT_TEXT));

后面觉得在这上面耗着不行,只能换个思路,试试不用HttpClient的情况下是调用form-data类型的接口会不会有问题:

/*** 不使用类库,通过java的原生api实现form-data请求*/
public static String postDataByForm(String url, Map<String, Object> paramMap) throws IOException {String res = "";HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();// 设置请求方法为POSTconnection.setRequestMethod("POST");final String BOUNDARY = "----WebKitFormBoundary7MA4YWxkTrZu0gW";// 设置请求属性,模拟form-data提交connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);// 设置允许输出connection.setDoOutput(true);// 构建form-data的内容StringBuilder data = new StringBuilder();for (Map.Entry<String, Object> entry : paramMap.entrySet()) {data.append("--").append(BOUNDARY).append("\r\nContent-Disposition: form-data; name=\"").append(entry.getKey()).append("\"\r\n\r\n").append(entry.getValue()).append("\r\n");}data.append("--").append(BOUNDARY).append("\r\n");// 写入请求体try (OutputStream os = connection.getOutputStream()) {os.write(data.toString().getBytes());}// 获取响应码int responseCode = connection.getResponseCode();System.out.println("Response Code: " + responseCode);// 读取返回数据StringBuilder strBuf = new StringBuilder();BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));String line;while ((line = reader.readLine()) != null) {strBuf.append(line).append("\n");}res = strBuf.toString();// 断开连接connection.disconnect();return res;}

结果当然是对方能正常接收。。。
正常接收的结果
因此肯定是使用HttpClient时,某个地方少添加了设置。通过查看原生java实现form-data接口的调用后,猜测应该是Content-Disposition或者boundary的问题,然后我想到借助ChatGpt的能力提供解决方案。
复制了代码问GPT
让它为我提供修改意见
借助ChatGpt获取的答案

总结及反思

最后按这种方式也的确解决了问题,但不明白既然请求form-data类型的接口需要用到boundary,为什么HttpClient不实现这部分内容呢?
看了MultipartEntityBuilderbuild()方法源码,是会判断boundary如果为空,会生成一个随机值,只是这个值没有getter方法可获取。所以还是要在HttpPost对象进行boundary的设置,否则请求体和请求头的值会不一致。问题还是出现在HttpPost不会自动获取请求体的boundary导致的需要手动设置,想了下原因,可能是HttpClient为了解耦,让两个类的标识符要单独设置吧。大家有其他的想法也可以留下您们的评论。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/669459.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我希望未来10年,人工智能可以帮我解决这4件小事

生活在一线大城市的我&#xff0c;现在几乎整天被大数据、人工智能、机器学习、智慧生活的词汇环绕立体包围着&#xff0c;让我时刻感觉到&#xff0c;再过10年&#xff0c;我们五一假期真的可以摆脱现在擦肩接踵的旅游盛况了。但我其实要求倒是没这么高&#xff0c;我真心希望…

漏洞挖掘 | 某米企业src未授权访问

某米企业src漏洞挖掘 这一挖就挖到了一个未授权操作漏洞&#xff0c;写个文章记录下~~ 通过信息收集&#xff0c;发现这么一个资产。 访问 http://xxx.com 如下图所示 1.点击头像-点击授权登录 2.然后发现可删除大量授权的用户信息&#xff0c;总计全部1292条&#xff0c;最…

三维SDMTSP:GWO灰狼优化算法求解三维单仓库多旅行商问题,可以更改数据集和起点(MATLAB代码)

灰狼优化算法的目标函数是各个旅行商路径之和 第1个旅行商的路径&#xff1a;10->18->17->22->14->4->10 第1个旅行商的总目标函数值&#xff1a;1063.936088 第2个旅行商的路径&#xff1a;10->19->25->11->15->2->10 第2个旅行商的总…

NineData亮相2024中国移动算力网络大会

4月28日至29日&#xff0c;2024中国移动算力网络大会在苏州召开。大会以“算力网络点亮AI新时代”为主题&#xff0c;全面展示了中国移动最新算力网络成果与能力。江苏省委常委、苏州市委书记刘小涛&#xff0c;副省长赵岩出席开幕式并致辞。内蒙古自治区副主席白清元出席。中国…

vue快速入门(五十五)插槽基本用法

注释很详细&#xff0c;直接上代码 上一篇 新增内容 当传输内容只有一种时的基础写法 源码 App.vue <template><div id"app"><h1>被淡化的背景内容</h1><my-dialog><!-- 插槽内容:文字以及dom结构都可以传 --><span>你确…

WPF之绑定属性值转换

1&#xff0c;使用Binding.Format属性简易设置绑定的属性数据显示格式。 <TextBox Grid.Row"2" Grid.Column"1"><TextBox.Text><Binding Path"UnitCost" StringFormat"{}{0:C3}" > …

QT开发(四) 制作一个JSON检查小工具

1、JSON概念 1.1 定义 JSON&#xff08;JavaScript Object Notation&#xff09;是一种轻量级的数据交换格式&#xff0c;它易于人类阅读和编写&#xff0c;同时也易于机器解析和生成。JSON基于JavaScript语言的子集&#xff0c;但是独立于编程语言&#xff0c;因此可以被多种…

【C语言】解决不同场景字符串问题:巧妙运用字符串函数

&#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&#xff1a; C笔记 &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 文章目录 一、字符函数1.1 字符分类函数1.1.1 islower1.1.2 isupper 1.…

JuiceFS v1.2-beta1,Gateway 升级,多用户场景权限管理更灵活

JuiceFS v1.2-beta1 今天正式发布。在这个版本中&#xff0c;除了进行了大量使用体验优化和 bug 修复外&#xff0c;新增三个特性&#xff1a; Gateway 功能扩展&#xff1a;新增了“身份和访问管理&#xff08;Identity and Access Management&#xff0c;IAM&#xff09;” 与…

OpenNJet:新一代云原生应用引擎

OpenNJet&#xff1a;新一代云原生应用引擎 云原生应用引擎&#xff1f;&#xff1f;云原生应用引擎&#xff01;&#xff01;那么首先&#xff0c;什么是云原生&#xff1f;然后&#xff0c;什么是应用引擎&#xff1f;最后&#xff0c;OpenNJet&#xff01;&#xff01; 百闻…

本地运行Llama3极简中文傻瓜手把手教程,超越GPT4?能力如何?|文末赠2024AI工程师完整视频教程+源码资料

目录 文末福利&#xff1a;文末赠2024AI工程师完整视频教程源码资料 Ollama 在本地简单的运行llama 安装 运行llama3 加个web ui来运行&#xff0c;更直观&#xff01; 下载open-webui docker镜像 启动镜像 访问 配置 测试效果 将 Ollama 与 Python 结合使用 和Lla…

订单超时自动取消的实践方案

1、定时任务方案 方案流程&#xff1a; 每隔 30 秒查询数据库&#xff0c;取出最近的 N 条未支付的订单。 遍历查询出来的订单列表&#xff0c;判断当前时间减去订单的创建时间是否超过了支付超时时间&#xff0c;如果超时则对该订单执行取消操作。 定时任务方案工程实现相…