diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/body/HttpBody.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/body/HttpBody.java
index ab323b4b8..071e6e60d 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/body/HttpBody.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/body/HttpBody.java
@@ -14,6 +14,7 @@ package org.dromara.hutool.http.client.body;
import org.dromara.hutool.core.io.stream.FastByteArrayOutputStream;
import org.dromara.hutool.core.io.IoUtil;
+import org.dromara.hutool.core.text.StrUtil;
import java.io.InputStream;
import java.io.OutputStream;
@@ -32,23 +33,39 @@ public interface HttpBody {
void write(OutputStream out);
/**
- * 获取Content-Type
+ * 获取Content-Type
+ * 根据实现不同,Content-Type可能包含编码信息,也可以不包含
*
* @return Content-Type值
*/
String getContentType();
/**
- * 获取指定编码的Content-Type,类似于:application/json;charset=UTF-8
+ * 获取指定编码的Content-Type,类似于:application/json;charset=UTF-8
+ * 如果{@link #getContentType()} 已经包含编码信息,则编码信息一致直接返回,否则替换为指定编码。
+ *
* @param charset 编码
* @return Content-Type
+ * @see #getContentType()
*/
- default String getContentType(final Charset charset){
- final String contentType = getContentType();
- if(null == contentType){
+ default String getContentType(final Charset charset) {
+ String contentType = getContentType();
+ if (null == contentType) {
return null;
}
+ final String charsetName = charset.name();
+ if (StrUtil.endWithIgnoreCase(contentType, charsetName) || StrUtil.containsIgnoreCase(contentType, "boundary=")) {
+ // 已经包含编码信息,且编码一致,无需再次添加
+ // multipart无需添加charset
+ return contentType;
+ }
+
+ if (StrUtil.containsIgnoreCase(contentType, ";charset=")) {
+ // 已经包含编码信息,但编码不一致,需要替换
+ contentType = StrUtil.subBefore(contentType, ";charset=", true);
+ }
+
return contentType + ";charset=" + charset.name();
}
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/client/body/MultipartBody.java b/hutool-http/src/main/java/org/dromara/hutool/http/client/body/MultipartBody.java
index c1bbbd0ca..dae5d1e70 100644
--- a/hutool-http/src/main/java/org/dromara/hutool/http/client/body/MultipartBody.java
+++ b/hutool-http/src/main/java/org/dromara/hutool/http/client/body/MultipartBody.java
@@ -84,6 +84,12 @@ public class MultipartBody extends FormBody {
return CONTENT_TYPE_MULTIPART_PREFIX + boundary;
}
+ @Override
+ public String getContentType(final Charset charset) {
+ // multipart的Content-Type头指定"Content-Type; boundary=XXXX",编码无效
+ return getContentType();
+ }
+
/**
* 写出Multiparty数据,不关闭流
*
diff --git a/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3658Test.java b/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3658Test.java
new file mode 100644
index 000000000..b0ae3a34f
--- /dev/null
+++ b/hutool-http/src/test/java/org/dromara/hutool/http/client/Issue3658Test.java
@@ -0,0 +1,24 @@
+package org.dromara.hutool.http.client;
+
+import org.dromara.hutool.core.collection.CollUtil;
+import org.dromara.hutool.http.meta.HeaderName;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class Issue3658Test {
+
+ @Test
+ public void name() {
+ // 请注意这里一定使用带form参数的get方法,参数随便设什么都行,url也随便
+ final Request request = Request.of("https://timor.tech/api/holiday/year/2020-02")
+ .form(Collections.singletonMap("11", "22"));
+
+ final Collection collection = request.headers().get(HeaderName.CONTENT_TYPE.getValue());
+ final String s = CollUtil.get(collection, 0);
+ Assertions.assertEquals("application/x-www-form-urlencoded;charset=UTF-8", s);
+ }
+
+}