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();
		// 设置请求方法为POST
		connection.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.mfbz.cn/a/595144.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【参赛总结】第二届云原生编程挑战赛-冷热读写场景的RocketMQ存储系统设计 - Nico

关联比赛: 2021第二届云原生编程挑战赛1&#xff1a;针对冷热读写场景的RocketMQ存储系统设计 引子 在一个浑浑噩噩的下午&#xff0c;百无聊赖的我像往常一样点开了划水交流群&#xff0c;细细品味着老哥们关于量子力学的讨论。嬉戏间&#xff0c;平常水不拉几的群友张三忽…

【毕业设计】基于SSM的运动用品商城的设计与实现

1.项目介绍 在这个日益数字化和信息化的时代&#xff0c;随着人们购物习惯的转变&#xff0c;传统的实体商店已经无法满足人们日益增长的在线购物需求。因此&#xff0c;基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的运动用品商城项目应运而生&#xff0…

OpenGL 入门(二)—— 渲染摄像头采集的预览画面

本篇主要内容&#xff1a; 将摄像头采集到的图像通过 OpenGL 绘制到屏幕上FBO 离屏渲染 在开始上述流程前&#xff0c;我们有必要对 SurfaceTexture 做一个简单了解&#xff0c;因为 OpenGL 需要通过它获取要绘制的图像。 1、认识 SurfaceTexture SurfaceTexture 是 Androi…

【XR806开发板试用】XR806与鸿蒙,创建任务,串口转发TCPServer收到的数据

很荣幸获得评测开发板的机会&#xff0c;XR806的程序资料做的还是挺不错的。 目标&#xff1a; 1、学习用鸿蒙创建2个任务&#xff1b; 2、创建TCP Server收发数据。 任务ledThread&#xff1a;LED每秒亮灭一次&#xff0c;代表程序在运行。 任务MainThread&#xff1a;创建TCP…

Leetcode—377. 组合总和 Ⅳ【中等】

2024每日刷题&#xff08;124&#xff09; Leetcode—377. 组合总和 Ⅳ 算法思想 实现代码 class Solution { public:int combinationSum4(vector<int>& nums, int target) {vector<unsigned long long>dp(target 1);dp[0] 1;for(int i 1; i < target;…

echarts柱状图实现左右横向对比

实现效果如上图 其实是两组数据&#xff0c;其中一组数据改为负数&#xff0c;然后 在展示的时候&#xff0c;在将负数取反 第一处修改坐标轴 xAxis: [{type: value,axisLabel: {formatter: function (value) {if (value < 0) {return -value;}else{return value;}}}}], 第…

如何修改图片大小?调整图片大小的几个方法介绍

当我们在不同的应用场景中使用图片的时候&#xff0c;常常会需要去调整图片尺寸来适应不同的要求&#xff0c;还有图片体积大小也会有要求&#xff0c;这时候就需要用到我们今天分享的这款图片在线处理工具了&#xff0c;不管是图片改大小或者图片压缩它都能快速解决&#xff0…

LVGL移植到STM32F4

1、LVGL简介 LittlevGL是一个免费的开源图形库&#xff0c;提供了创建嵌入式GUI所需的一切&#xff0c;具有易于使用的图形元素、漂亮的视觉效果和低内存占用。 1.1、LVGL特点 强大的构建模组&#xff1a;按钮、图表、列表、滑块、图像等先进的图形&#xff1a;动画、反锯齿…

【热门话题】ElementUI 快速入门指南

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 ElementUI 快速入门指南环境准备安装 ElementUI创建 Vue 项目安装 ElementUI 基…

自然语言(NLP)

It’s time for us to learn how to analyse natural language documents, using Natural Language Processing (NLP). We’ll be focusing on the Hugging Face ecosystem, especially the Transformers library, and the vast collection of pretrained NLP models. Our proj…

STM32单片机实战开发笔记-独立看门狗IWDG

嵌入式单片机开发实战例程合集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/11av8rV45dtHO0EHf8e_Q0Q?pwd28ab 提取码&#xff1a;28ab IWDG模块测试 1、功能描述 STM32F10X内置两个看门狗&#xff0c;提供了更高的安全性&#xff0c;时间的精确下性和使用的灵活性…

微信答题链接怎么做_新手也能快速上手制作

在数字营销日新月异的今天&#xff0c;如何有效吸引用户参与、提升品牌曝光度&#xff0c;成为了每一个营销人都在思考的问题。而微信答题链接&#xff0c;作为一种新兴的互动营销方式&#xff0c;正以其独特的魅力&#xff0c;在营销界掀起一股新的热潮。今天&#xff0c;就让…

第三节课,前端

一、参考链接&#xff1b; 总 知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff0c;知识变现的工具 分 2022-03-18 星球直播笔记-用户中心&#xff08;下&#xff09; 语雀 二、登录 2.1登录网址 2.2前端页面修改 2.1 页面修改 2.2 页脚的超链接 网址&am…

Window如何运行sh文件以及wget指令

Git下载 官网链接如下&#xff1a;https://gitforwindows.org/ 安装就保持一路无脑安装就行&#xff0c;不需要改变安装过程中的任何一个选项。 配置Git 切刀桌面&#xff0c;随便右击屏幕空白处&#xff0c;点open Git Bash here 把这行复制过去&#xff0c;回车&#xff1…

【源码+文档+调试教程】基于微信小程序的电子购物系统的设计与实现

摘 要 由于APP软件在开发以及运营上面所需成本较高&#xff0c;而用户手机需要安装各种APP软件&#xff0c;因此占用用户过多的手机存储空间&#xff0c;导致用户手机运行缓慢&#xff0c;体验度比较差&#xff0c;进而导致用户会卸载非必要的APP&#xff0c;倒逼管理者必须改…

本地的git仓库和远程仓库

文章目录 1. 远程创建仓库2. 关联远程和本地代码3. 推送本地分支到远程 1. 远程创建仓库 2. 关联远程和本地代码 上面创建完后会得到一个git仓库的链接&#xff0c;有SSH或者http的 http://gitlab.xxxxx.local:18080/xxxxx/dvr_avm.git ssh://gitgitlab.xxxxx.local:10022/xx…

COUNT(1)\COUNT(*)\COUNT(列名)到底谁更快

今天来研究一个比较有趣的话题,关于我们平常使用mysql查询数量的到底那种方式查询效率更高的问题 起因 这个问题在我以前的认知里是,按效率从高到低品排序 count(1)>count(列名)>count(*),但是我也注意到过mybatis-plus官方提供的selectCount方法和分页查询时,它的SQL在…

第五十三节 Java设计模式 - 工厂模式

Java设计模式 - 工厂模式 工厂模式是一种创建模式&#xff0c;因为此模式提供了更好的方法来创建对象。 在工厂模式中&#xff0c;我们创建对象而不将创建逻辑暴露给客户端。 例子 在以下部分中&#xff0c;我们将展示如何使用工厂模式创建对象。 由工厂模式创建的对象将是…

监控公司局域网电脑的软件|局域网电脑监控软件哪个好用

想要监控公司局域网电脑&#xff1f;没问题&#xff0c;市面上有一大堆选择等着你&#xff01;每个软件都有它的独门绝技和适用场合&#xff0c;接下来就让我带你看看哪些软件既好用又功能强大吧&#xff01; &#x1f389;OpManager&#xff1a; 这位大佬适合中大型企业&#…

每日算法-java

题目来自蓝桥云 // 这是一个Java程序&#xff0c;用于解决最长不下降子序列问题。 // 问题描述&#xff1a;给定一个整数序列&#xff0c;找到最长的子序列&#xff0c;使得这个子序列是不下降的&#xff08;即相邻的元素不严格递减&#xff09;。 // 程序使用了动态规划的方法…
最新文章