mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix code
This commit is contained in:
parent
185cd09ab0
commit
75d536839c
@ -21,12 +21,14 @@ import java.util.stream.Collectors;
|
||||
* <p>当通过实例方法获得值集合时,若该集合允许修改,则对值集合的修改将会影响到其所属的{@link MultiValueMap}实例,反之亦然。
|
||||
* 因此当同时遍历当前实例或者值集合时,若存在写操作,则需要注意可能引发的{@link ConcurrentModificationException}。
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author huangchengxing
|
||||
* @since 6.0.0
|
||||
* @see AbsCollValueMap
|
||||
* @see CollectionValueMap
|
||||
* @see ListValueMap
|
||||
* @see SetValueMap
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
|
||||
@ -64,7 +66,7 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
* Collection<V> coll = entry.getValues();
|
||||
* for (V val : coll) {
|
||||
* map.putValue(key, val)
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
@ -81,7 +83,7 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
* <pre>{@code
|
||||
* for (V val : coll) {
|
||||
* map.putValue(key, val)
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param key 键
|
||||
@ -95,7 +97,7 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
* <pre>{@code
|
||||
* for (V val : values) {
|
||||
* map.putValue(key, val)
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @param key 键
|
||||
@ -137,7 +139,7 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
/**
|
||||
* 将一批值从指定键下的值集合中删除
|
||||
*
|
||||
* @param key 键
|
||||
* @param key 键
|
||||
* @param values 值数组
|
||||
* @return 是否成功删除
|
||||
*/
|
||||
@ -149,7 +151,7 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
/**
|
||||
* 将一批值从指定键下的值集合中删除
|
||||
*
|
||||
* @param key 键
|
||||
* @param key 键
|
||||
* @param values 值集合
|
||||
* @return 是否成功删除
|
||||
*/
|
||||
@ -236,7 +238,7 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
* Collection<V> coll = entry.getValues();
|
||||
* for (V val : coll) {
|
||||
* consumer.accept(key, val);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
@ -259,8 +261,8 @@ public interface MultiValueMap<K, V> extends Map<K, Collection<V>> {
|
||||
*/
|
||||
default Collection<V> allValues() {
|
||||
return values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -558,7 +558,6 @@ public class URLUtil {
|
||||
|
||||
if (StrUtil.isNotEmpty(body)) {
|
||||
// 去除开头的\或者/
|
||||
//noinspection ConstantConditions
|
||||
body = body.replaceAll("^[\\\\/]+", StrUtil.EMPTY);
|
||||
// 替换\为/
|
||||
body = body.replace("\\", "/");
|
||||
|
215
hutool-core/src/main/java/cn/hutool/core/net/url/UrlQueryUtil.java
Executable file
215
hutool-core/src/main/java/cn/hutool/core/net/url/UrlQueryUtil.java
Executable file
@ -0,0 +1,215 @@
|
||||
package cn.hutool.core.net.url;
|
||||
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class UrlQueryUtil {
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式,会自动url编码键和值
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @return url参数
|
||||
*/
|
||||
public static String toQuery(final Map<String, ?> paramMap) {
|
||||
return toQuery(paramMap, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||
* 会自动url编码键和值<br>
|
||||
* 此方法用于拼接URL中的Query部分,并不适用于POST请求中的表单
|
||||
*
|
||||
* <pre>
|
||||
* key1=v1&key2=&key3=v3
|
||||
* </pre>
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @param charset 编码,{@code null} 表示不encode键值对
|
||||
* @return url参数
|
||||
* @see #toQuery(Map, Charset, boolean)
|
||||
*/
|
||||
public static String toQuery(final Map<String, ?> paramMap, final Charset charset) {
|
||||
return toQuery(paramMap, charset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||
* 会自动url编码键和值
|
||||
*
|
||||
* <pre>
|
||||
* key1=v1&key2=&key3=v3
|
||||
* </pre>
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @param charset 编码,null表示不encode键值对
|
||||
* @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
|
||||
* @return url参数
|
||||
* @since 5.7.16
|
||||
*/
|
||||
public static String toQuery(final Map<String, ?> paramMap, final Charset charset, final boolean isFormUrlEncoded) {
|
||||
return UrlQuery.of(paramMap, isFormUrlEncoded).build(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对URL参数做编码,只编码键和值<br>
|
||||
* 提供的值可以是url附带参数,但是不能只是url
|
||||
*
|
||||
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||
*
|
||||
* @param urlWithParams url和参数,可以包含url本身,也可以单独参数
|
||||
* @param charset 编码
|
||||
* @return 编码后的url和参数
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String encodeQuery(final String urlWithParams, final Charset charset) {
|
||||
if (StrUtil.isBlank(urlWithParams)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
String urlPart = null; // url部分,不包括问号
|
||||
String paramPart; // 参数部分
|
||||
final int pathEndPos = urlWithParams.indexOf('?');
|
||||
if (pathEndPos > -1) {
|
||||
// url + 参数
|
||||
urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
|
||||
paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
|
||||
if (StrUtil.isBlank(paramPart)) {
|
||||
// 无参数,返回url
|
||||
return urlPart;
|
||||
}
|
||||
} else if (false == StrUtil.contains(urlWithParams, '=')) {
|
||||
// 无参数的URL
|
||||
return urlWithParams;
|
||||
} else {
|
||||
// 无URL的参数
|
||||
paramPart = urlWithParams;
|
||||
}
|
||||
|
||||
paramPart = normalizeQuery(paramPart, charset);
|
||||
|
||||
return StrUtil.isBlank(urlPart) ? paramPart : urlPart + "?" + paramPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化参数字符串,即URL中?后的部分
|
||||
*
|
||||
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||
*
|
||||
* @param queryPart 参数字符串
|
||||
* @param charset 编码
|
||||
* @return 标准化的参数字符串
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static String normalizeQuery(final String queryPart, final Charset charset) {
|
||||
if (StrUtil.isEmpty(queryPart)) {
|
||||
return queryPart;
|
||||
}
|
||||
final StringBuilder builder = new StringBuilder(queryPart.length() + 16);
|
||||
final int len = queryPart.length();
|
||||
String name = null;
|
||||
int pos = 0; // 未处理字符开始位置
|
||||
char c; // 当前字符
|
||||
int i; // 当前字符位置
|
||||
for (i = 0; i < len; i++) {
|
||||
c = queryPart.charAt(i);
|
||||
if (c == '=') { // 键值对的分界点
|
||||
if (null == name) {
|
||||
// 只有=前未定义name时被当作键值分界符,否则做为普通字符
|
||||
name = (pos == i) ? StrUtil.EMPTY : queryPart.substring(pos, i);
|
||||
pos = i + 1;
|
||||
}
|
||||
} else if (c == '&') { // 参数对的分界点
|
||||
if (pos != i) {
|
||||
if (null == name) {
|
||||
// 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
|
||||
name = queryPart.substring(pos, i);
|
||||
builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=');
|
||||
} else {
|
||||
builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=')
|
||||
.append(RFC3986.QUERY_PARAM_VALUE.encode(queryPart.substring(pos, i), charset)).append('&');
|
||||
}
|
||||
name = null;
|
||||
}
|
||||
pos = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 结尾处理
|
||||
if (null != name) {
|
||||
builder.append(URLEncoder.encodeQuery(name, charset)).append('=');
|
||||
}
|
||||
if (pos != i) {
|
||||
if (null == name && pos > 0) {
|
||||
builder.append('=');
|
||||
}
|
||||
builder.append(URLEncoder.encodeQuery(queryPart.substring(pos, i), charset));
|
||||
}
|
||||
|
||||
// 以&结尾则去除之
|
||||
final int lastIndex = builder.length() - 1;
|
||||
if ('&' == builder.charAt(lastIndex)) {
|
||||
builder.delete(lastIndex, builder.length());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Map<String, String> decodeQuery(final String paramsStr, final Charset charset) {
|
||||
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||
if (MapUtil.isEmpty(queryMap)) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
return Convert.toMap(String.class, String.class, queryMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
*/
|
||||
public static Map<String, List<String>> decodeQuery(final String paramsStr, final String charset) {
|
||||
return decodeQueryList(paramsStr, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Map<String, List<String>> decodeQueryList(final String paramsStr, final Charset charset) {
|
||||
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||
if (MapUtil.isEmpty(queryMap)) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
|
||||
final Map<String, List<String>> params = new LinkedHashMap<>();
|
||||
queryMap.forEach((key, value) -> {
|
||||
final List<String> values = params.computeIfAbsent(StrUtil.str(key), k -> new ArrayList<>(1));
|
||||
// 一般是一个参数
|
||||
values.add(StrUtil.str(value));
|
||||
});
|
||||
return params;
|
||||
}
|
||||
}
|
153
hutool-core/src/test/java/cn/hutool/core/net/url/UrlQueryUtilTest.java
Executable file
153
hutool-core/src/test/java/cn/hutool/core/net/url/UrlQueryUtilTest.java
Executable file
@ -0,0 +1,153 @@
|
||||
package cn.hutool.core.net.url;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class UrlQueryUtilTest {
|
||||
@Test
|
||||
public void decodeQueryTest() {
|
||||
final String paramsStr = "uuuu=0&a=b&c=%3F%23%40!%24%25%5E%26%3Ddsssss555555";
|
||||
final Map<String, List<String>> map = UrlQueryUtil.decodeQuery(paramsStr, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("0", map.get("uuuu").get(0));
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("?#@!$%^&=dsssss555555", map.get("c").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeQueryTest2() {
|
||||
// 参数值存在分界标记等号时
|
||||
final Map<String, String> paramMap = UrlQueryUtil.decodeQuery("https://www.xxx.com/api.action?aa=123&f_token=NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=", CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("123",paramMap.get("aa"));
|
||||
Assert.assertEquals("NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=",paramMap.get("f_token"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toQueryTest() {
|
||||
final String paramsStr = "uuuu=0&a=b&c=3Ddsssss555555";
|
||||
final Map<String, List<String>> map = UrlQueryUtil.decodeQuery(paramsStr, CharsetUtil.NAME_UTF_8);
|
||||
|
||||
final String encodedParams = UrlQueryUtil.toQuery(map);
|
||||
Assert.assertEquals(paramsStr, encodedParams);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeParamTest() {
|
||||
// ?单独存在去除之,&单位位于末尾去除之
|
||||
String paramsStr = "?a=b&c=d&";
|
||||
String encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=b&c=d", encode);
|
||||
|
||||
// url不参与转码
|
||||
paramsStr = "http://www.abc.dd?a=b&c=d&";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("http://www.abc.dd?a=b&c=d", encode);
|
||||
|
||||
// b=b中的=被当作值的一部分,不做encode
|
||||
paramsStr = "a=b=b&c=d&";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=b=b&c=d", encode);
|
||||
|
||||
// =d的情况被处理为key为空
|
||||
paramsStr = "a=bbb&c=d&=d";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&=d", encode);
|
||||
|
||||
// d=的情况被处理为value为空
|
||||
paramsStr = "a=bbb&c=d&d=";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&d=", encode);
|
||||
|
||||
// 多个&&被处理为单个,相当于空条件
|
||||
paramsStr = "a=bbb&c=d&&&d=";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&d=", encode);
|
||||
|
||||
// &d&相当于只有键,无值得情况
|
||||
paramsStr = "a=bbb&c=d&d&";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&d=", encode);
|
||||
|
||||
// 中文的键和值被编码
|
||||
paramsStr = "a=bbb&c=你好&哈喽&";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=", encode);
|
||||
|
||||
// URL原样输出
|
||||
paramsStr = "https://www.hutool.cn/";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals(paramsStr, encode);
|
||||
|
||||
// URL原样输出
|
||||
paramsStr = "https://www.hutool.cn/?";
|
||||
encode = UrlQueryUtil.encodeQuery(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("https://www.hutool.cn/", encode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeParamTest() {
|
||||
// 开头的?被去除
|
||||
String a = "?a=b&c=d&";
|
||||
Map<String, List<String>> map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
|
||||
// =e被当作空为key,e为value
|
||||
a = "?a=b&c=d&=e";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("e", map.get("").get(0));
|
||||
|
||||
// 多余的&去除
|
||||
a = "?a=b&c=d&=e&&&&";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("e", map.get("").get(0));
|
||||
|
||||
// 值为空
|
||||
a = "?a=b&c=d&e=";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("", map.get("e").get(0));
|
||||
|
||||
// &=被作为键和值都为空
|
||||
a = "a=b&c=d&=";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("", map.get("").get(0));
|
||||
|
||||
// &e&这类单独的字符串被当作key
|
||||
a = "a=b&c=d&e&";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertNull(map.get("e").get(0));
|
||||
Assert.assertNull(map.get("").get(0));
|
||||
|
||||
// 被编码的键和值被还原
|
||||
a = "a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("bbb", map.get("a").get(0));
|
||||
Assert.assertEquals("你好", map.get("c").get(0));
|
||||
Assert.assertEquals("", map.get("哈喽").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalizeQueryTest() {
|
||||
final String encodeResult = UrlQueryUtil.normalizeQuery("参数", CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("%E5%8F%82%E6%95%B0", encodeResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalizeBlankQueryTest() {
|
||||
final String encodeResult = UrlQueryUtil.normalizeQuery("", CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("", encodeResult);
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package cn.hutool.http;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.meta.Header;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -15,7 +14,7 @@ import java.util.Map.Entry;
|
||||
|
||||
/**
|
||||
* 全局头部信息<br>
|
||||
* 所有Http请求将共用此全局头部信息,除非在{@link HttpRequest}中自定义头部信息覆盖之
|
||||
* 所有Http请求将共用此全局头部信息
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
|
@ -1,34 +1,26 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.codec.BaseN.Base64;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.RFC3986;
|
||||
import cn.hutool.core.net.url.URLEncoder;
|
||||
import cn.hutool.core.net.url.UrlQuery;
|
||||
import cn.hutool.core.net.url.UrlQueryUtil;
|
||||
import cn.hutool.core.regex.ReUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.client.HttpDownloader;
|
||||
import cn.hutool.http.client.ClientConfig;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.client.cookie.GlobalCookieManager;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.engine.ClientEngineFactory;
|
||||
import cn.hutool.http.meta.ContentType;
|
||||
import cn.hutool.http.meta.Method;
|
||||
import cn.hutool.http.server.SimpleServer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.CookieManager;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -69,52 +61,6 @@ public class HttpUtil {
|
||||
return StrUtil.startWithIgnoreCase(url, "http:");
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Http请求对象
|
||||
*
|
||||
* @param method 方法枚举{@link Method}
|
||||
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||
* @return {@link HttpRequest}
|
||||
* @since 3.0.9
|
||||
*/
|
||||
public static HttpRequest createRequest(final Method method, final String url) {
|
||||
return HttpRequest.of(url).method(method);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Http GET请求对象
|
||||
*
|
||||
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||
* @return {@link HttpRequest}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public static HttpRequest createGet(final String url) {
|
||||
return createGet(url, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Http GET请求对象
|
||||
*
|
||||
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||
* @param isFollowRedirects 是否打开重定向
|
||||
* @return {@link HttpRequest}
|
||||
* @since 5.6.4
|
||||
*/
|
||||
public static HttpRequest createGet(final String url, final boolean isFollowRedirects) {
|
||||
return HttpRequest.get(url).setFollowRedirects(isFollowRedirects);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Http POST请求对象
|
||||
*
|
||||
* @param url 请求的URL,可以使HTTP或者HTTPS
|
||||
* @return {@link HttpRequest}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
public static HttpRequest createPost(final String url) {
|
||||
return HttpRequest.post(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送get请求
|
||||
*
|
||||
@ -124,7 +70,7 @@ public class HttpUtil {
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String get(final String urlString, final Charset customCharset) {
|
||||
return HttpRequest.get(urlString).charset(customCharset).execute().bodyStr();
|
||||
return send(Request.of(urlString).charset(customCharset)).bodyStr();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -147,7 +93,9 @@ public class HttpUtil {
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String get(final String urlString, final int timeout) {
|
||||
return HttpRequest.get(urlString).timeout(timeout).execute().bodyStr();
|
||||
return ClientEngineFactory.get()
|
||||
.setConfig(ClientConfig.of().setConnectionTimeout(timeout).setReadTimeout(timeout))
|
||||
.send(Request.of(urlString)).bodyStr();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -159,21 +107,8 @@ public class HttpUtil {
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String get(final String urlString, final Map<String, Object> paramMap) {
|
||||
return HttpRequest.get(urlString).form(paramMap).execute().bodyStr();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送get请求
|
||||
*
|
||||
* @param urlString 网址
|
||||
* @param paramMap post表单数据
|
||||
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||
* @return 返回数据
|
||||
* @since 3.3.0
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String get(final String urlString, final Map<String, Object> paramMap, final int timeout) {
|
||||
return HttpRequest.get(urlString).form(paramMap).timeout(timeout).execute().bodyStr();
|
||||
return send(Request.of(urlString).form(paramMap))
|
||||
.bodyStr();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,22 +118,10 @@ public class HttpUtil {
|
||||
* @param paramMap post表单数据
|
||||
* @return 返回数据
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String post(final String urlString, final Map<String, Object> paramMap) {
|
||||
return post(urlString, paramMap, HttpGlobalConfig.getTimeout());
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送post请求
|
||||
*
|
||||
* @param urlString 网址
|
||||
* @param paramMap post表单数据
|
||||
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||
* @return 返回数据
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String post(final String urlString, final Map<String, Object> paramMap, final int timeout) {
|
||||
return HttpRequest.post(urlString).form(paramMap).timeout(timeout).execute().bodyStr();
|
||||
return send(Request.of(urlString).method(Method.POST).form(paramMap))
|
||||
.bodyStr();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -214,426 +137,21 @@ public class HttpUtil {
|
||||
* @param body post表单数据
|
||||
* @return 返回数据
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String post(final String urlString, final String body) {
|
||||
return post(urlString, body, HttpGlobalConfig.getTimeout());
|
||||
return send(Request.of(urlString).method(Method.POST).body(body))
|
||||
.bodyStr();
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送post请求<br>
|
||||
* 请求体body参数支持两种类型:
|
||||
* 使用默认HTTP引擎,发送HTTP请求
|
||||
*
|
||||
* <pre>
|
||||
* 1. 标准参数,例如 a=1&b=2 这种格式
|
||||
* 2. Rest模式,此时body需要传入一个JSON或者XML字符串,Hutool会自动绑定其对应的Content-Type
|
||||
* </pre>
|
||||
*
|
||||
* @param urlString 网址
|
||||
* @param body post表单数据
|
||||
* @param timeout 超时时长,-1表示默认超时,单位毫秒
|
||||
* @return 返回数据
|
||||
* @since 3.2.0
|
||||
* @param request HTTP请求
|
||||
* @return HTTP响应
|
||||
* @see ClientEngineFactory#get()#send(Request)
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public static String post(final String urlString, final String body, final int timeout) {
|
||||
return HttpRequest.post(urlString).timeout(timeout).body(body).execute().bodyStr();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------- download
|
||||
|
||||
/**
|
||||
* 下载远程文本
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param customCharsetName 自定义的字符集
|
||||
* @return 文本
|
||||
*/
|
||||
public static String downloadString(final String url, final String customCharsetName) {
|
||||
return downloadString(url, CharsetUtil.charset(customCharsetName), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文本
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param customCharset 自定义的字符集,可以使用{@link CharsetUtil#charset} 方法转换
|
||||
* @return 文本
|
||||
*/
|
||||
public static String downloadString(final String url, final Charset customCharset) {
|
||||
return downloadString(url, customCharset, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文本
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param customCharset 自定义的字符集,可以使用{@link CharsetUtil#charset} 方法转换
|
||||
* @param streamPress 进度条 {@link StreamProgress}
|
||||
* @return 文本
|
||||
*/
|
||||
public static String downloadString(final String url, final Charset customCharset, final StreamProgress streamPress) {
|
||||
return HttpDownloader.downloadString(url, customCharset, streamPress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFile(final String url, final String dest) {
|
||||
return downloadFile(url, FileUtil.file(dest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFile(final String url, final File destFile) {
|
||||
return downloadFile(url, destFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @return 文件
|
||||
* @since 4.0.4
|
||||
*/
|
||||
public static File downloadFile(final String url, final File destFile, final int timeout) {
|
||||
return downloadFile(url, destFile, timeout, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFile(final String url, final File destFile, final StreamProgress streamProgress) {
|
||||
return downloadFile(url, destFile, -1, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件
|
||||
* @since 4.0.4
|
||||
*/
|
||||
public static File downloadFile(final String url, final File destFile, final int timeout, final StreamProgress streamProgress) {
|
||||
return HttpDownloader.downloadFile(url, destFile, timeout, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param dest 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @return 下载的文件对象
|
||||
* @since 5.4.1
|
||||
*/
|
||||
public static File downloadFileFromUrl(final String url, final String dest) {
|
||||
return downloadFileFromUrl(url, FileUtil.file(dest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @return 下载的文件对象
|
||||
* @since 5.4.1
|
||||
*/
|
||||
public static File downloadFileFromUrl(final String url, final File destFile) {
|
||||
return downloadFileFromUrl(url, destFile, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @return 下载的文件对象
|
||||
* @since 5.4.1
|
||||
*/
|
||||
public static File downloadFileFromUrl(final String url, final File destFile, final int timeout) {
|
||||
return downloadFileFromUrl(url, destFile, timeout, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param streamProgress 进度条
|
||||
* @return 下载的文件对象
|
||||
* @since 5.4.1
|
||||
*/
|
||||
public static File downloadFileFromUrl(final String url, final File destFile, final StreamProgress streamProgress) {
|
||||
return downloadFileFromUrl(url, destFile, -1, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param destFile 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @param streamProgress 进度条
|
||||
* @return 下载的文件对象
|
||||
* @since 5.4.1
|
||||
*/
|
||||
public static File downloadFileFromUrl(final String url, final File destFile, final int timeout, final StreamProgress streamProgress) {
|
||||
return HttpDownloader.downloadForFile(url, destFile, timeout, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||
* @param isCloseOut 是否关闭输出流
|
||||
* @return 文件大小
|
||||
*/
|
||||
public static long download(final String url, final OutputStream out, final boolean isCloseOut) {
|
||||
return download(url, out, isCloseOut, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param out 将下载内容写到输出流中 {@link OutputStream}
|
||||
* @param isCloseOut 是否关闭输出流
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件大小
|
||||
*/
|
||||
public static long download(final String url, final OutputStream out, final boolean isCloseOut, final StreamProgress streamProgress) {
|
||||
return HttpDownloader.download(url, out, isCloseOut, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件数据,支持30x跳转
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @return 文件数据
|
||||
* @since 5.3.6
|
||||
*/
|
||||
public static byte[] downloadBytes(final String url) {
|
||||
return HttpDownloader.downloadBytes(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式,会自动url编码键和值
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @return url参数
|
||||
*/
|
||||
public static String toParams(final Map<String, ?> paramMap) {
|
||||
return toParams(paramMap, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||
* 会自动url编码键和值<br>
|
||||
* 此方法用于拼接URL中的Query部分,并不适用于POST请求中的表单
|
||||
*
|
||||
* <pre>
|
||||
* key1=v1&key2=&key3=v3
|
||||
* </pre>
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @param charset 编码,{@code null} 表示不encode键值对
|
||||
* @return url参数
|
||||
* @see #toParams(Map, Charset, boolean)
|
||||
*/
|
||||
public static String toParams(final Map<String, ?> paramMap, final Charset charset) {
|
||||
return toParams(paramMap, charset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||
* 会自动url编码键和值
|
||||
*
|
||||
* <pre>
|
||||
* key1=v1&key2=&key3=v3
|
||||
* </pre>
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @param charset 编码,null表示不encode键值对
|
||||
* @param isFormUrlEncoded 是否为x-www-form-urlencoded模式,此模式下空格会编码为'+'
|
||||
* @return url参数
|
||||
* @since 5.7.16
|
||||
*/
|
||||
public static String toParams(final Map<String, ?> paramMap, final Charset charset, final boolean isFormUrlEncoded) {
|
||||
return UrlQuery.of(paramMap, isFormUrlEncoded).build(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对URL参数做编码,只编码键和值<br>
|
||||
* 提供的值可以是url附带参数,但是不能只是url
|
||||
*
|
||||
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||
*
|
||||
* @param urlWithParams url和参数,可以包含url本身,也可以单独参数
|
||||
* @param charset 编码
|
||||
* @return 编码后的url和参数
|
||||
* @since 4.0.1
|
||||
*/
|
||||
public static String encodeParams(final String urlWithParams, final Charset charset) {
|
||||
if (StrUtil.isBlank(urlWithParams)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
String urlPart = null; // url部分,不包括问号
|
||||
String paramPart; // 参数部分
|
||||
final int pathEndPos = urlWithParams.indexOf('?');
|
||||
if (pathEndPos > -1) {
|
||||
// url + 参数
|
||||
urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
|
||||
paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
|
||||
if (StrUtil.isBlank(paramPart)) {
|
||||
// 无参数,返回url
|
||||
return urlPart;
|
||||
}
|
||||
} else if (false == StrUtil.contains(urlWithParams, '=')) {
|
||||
// 无参数的URL
|
||||
return urlWithParams;
|
||||
} else {
|
||||
// 无URL的参数
|
||||
paramPart = urlWithParams;
|
||||
}
|
||||
|
||||
paramPart = normalizeParams(paramPart, charset);
|
||||
|
||||
return StrUtil.isBlank(urlPart) ? paramPart : urlPart + "?" + paramPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* 标准化参数字符串,即URL中?后的部分
|
||||
*
|
||||
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||
*
|
||||
* @param paramPart 参数字符串
|
||||
* @param charset 编码
|
||||
* @return 标准化的参数字符串
|
||||
* @since 4.5.2
|
||||
*/
|
||||
public static String normalizeParams(final String paramPart, final Charset charset) {
|
||||
if (StrUtil.isEmpty(paramPart)) {
|
||||
return paramPart;
|
||||
}
|
||||
final StringBuilder builder = new StringBuilder(paramPart.length() + 16);
|
||||
final int len = paramPart.length();
|
||||
String name = null;
|
||||
int pos = 0; // 未处理字符开始位置
|
||||
char c; // 当前字符
|
||||
int i; // 当前字符位置
|
||||
for (i = 0; i < len; i++) {
|
||||
c = paramPart.charAt(i);
|
||||
if (c == '=') { // 键值对的分界点
|
||||
if (null == name) {
|
||||
// 只有=前未定义name时被当作键值分界符,否则做为普通字符
|
||||
name = (pos == i) ? StrUtil.EMPTY : paramPart.substring(pos, i);
|
||||
pos = i + 1;
|
||||
}
|
||||
} else if (c == '&') { // 参数对的分界点
|
||||
if (pos != i) {
|
||||
if (null == name) {
|
||||
// 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
|
||||
name = paramPart.substring(pos, i);
|
||||
builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=');
|
||||
} else {
|
||||
builder.append(RFC3986.QUERY_PARAM_NAME.encode(name, charset)).append('=')
|
||||
.append(RFC3986.QUERY_PARAM_VALUE.encode(paramPart.substring(pos, i), charset)).append('&');
|
||||
}
|
||||
name = null;
|
||||
}
|
||||
pos = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 结尾处理
|
||||
if (null != name) {
|
||||
builder.append(URLEncoder.encodeQuery(name, charset)).append('=');
|
||||
}
|
||||
if (pos != i) {
|
||||
if (null == name && pos > 0) {
|
||||
builder.append('=');
|
||||
}
|
||||
builder.append(URLEncoder.encodeQuery(paramPart.substring(pos, i), charset));
|
||||
}
|
||||
|
||||
// 以&结尾则去除之
|
||||
final int lastIndex = builder.length() - 1;
|
||||
if ('&' == builder.charAt(lastIndex)) {
|
||||
builder.delete(lastIndex, builder.length());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Map<String, String> decodeParamMap(final String paramsStr, final Charset charset) {
|
||||
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||
if (MapUtil.isEmpty(queryMap)) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
return Convert.toMap(String.class, String.class, queryMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
*/
|
||||
public static Map<String, List<String>> decodeParams(final String paramsStr, final String charset) {
|
||||
return decodeParams(paramsStr, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Map<String, List<String>> decodeParams(final String paramsStr, final Charset charset) {
|
||||
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||
if (MapUtil.isEmpty(queryMap)) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
|
||||
final Map<String, List<String>> params = new LinkedHashMap<>();
|
||||
queryMap.forEach((key, value) -> {
|
||||
final List<String> values = params.computeIfAbsent(StrUtil.str(key), k -> new ArrayList<>(1));
|
||||
// 一般是一个参数
|
||||
values.add(StrUtil.str(value));
|
||||
});
|
||||
return params;
|
||||
public static Response send(final Request request){
|
||||
return ClientEngineFactory.get().send(request);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -649,11 +167,11 @@ public class HttpUtil {
|
||||
public static String urlWithForm(String url, final Map<String, Object> form, final Charset charset, final boolean isEncodeParams) {
|
||||
if (isEncodeParams && StrUtil.contains(url, '?')) {
|
||||
// 在需要编码的情况下,如果url中已经有部分参数,则编码之
|
||||
url = encodeParams(url, charset);
|
||||
url = UrlQueryUtil.encodeQuery(url, charset);
|
||||
}
|
||||
|
||||
// url和参数是分别编码的
|
||||
return urlWithForm(url, toParams(form, charset), charset, false);
|
||||
return urlWithForm(url, UrlQueryUtil.toQuery(form, charset), charset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -670,7 +188,7 @@ public class HttpUtil {
|
||||
// 无额外参数
|
||||
if (StrUtil.contains(url, '?')) {
|
||||
// url中包含参数
|
||||
return isEncode ? encodeParams(url, charset) : url;
|
||||
return isEncode ? UrlQueryUtil.encodeQuery(url, charset) : url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@ -680,7 +198,7 @@ public class HttpUtil {
|
||||
final int qmIndex = url.indexOf('?');
|
||||
if (qmIndex > 0) {
|
||||
// 原URL带参数,则对这部分参数单独编码(如果选项为进行编码)
|
||||
urlBuilder.append(isEncode ? encodeParams(url, charset) : url);
|
||||
urlBuilder.append(isEncode ? UrlQueryUtil.encodeQuery(url, charset) : url);
|
||||
if (false == StrUtil.endWith(url, '&')) {
|
||||
// 已经带参数的情况下追加参数
|
||||
urlBuilder.append('&');
|
||||
@ -693,7 +211,7 @@ public class HttpUtil {
|
||||
urlBuilder.append('?');
|
||||
}
|
||||
}
|
||||
urlBuilder.append(isEncode ? encodeParams(queryString, charset) : queryString);
|
||||
urlBuilder.append(isEncode ? UrlQueryUtil.encodeQuery(queryString, charset) : queryString);
|
||||
return urlBuilder.toString();
|
||||
}
|
||||
|
||||
|
@ -46,11 +46,11 @@ public class ClientConfig {
|
||||
/**
|
||||
* 是否禁用缓存
|
||||
*/
|
||||
public boolean disableCache;
|
||||
private boolean disableCache;
|
||||
/**
|
||||
* 代理
|
||||
*/
|
||||
public Proxy proxy;
|
||||
private Proxy proxy;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
|
@ -10,6 +10,13 @@ import java.io.Closeable;
|
||||
*/
|
||||
public interface ClientEngine extends Closeable {
|
||||
|
||||
/**
|
||||
* 设置客户端引擎参数,如超时、代理等信息
|
||||
* @param config 客户端设置
|
||||
* @return this
|
||||
*/
|
||||
ClientEngine setConfig(ClientConfig config);
|
||||
|
||||
/**
|
||||
* 发送HTTP请求
|
||||
* @param message HTTP请求消息
|
||||
|
@ -47,7 +47,7 @@ public interface HeaderOperation<T extends HeaderOperation<T>> {
|
||||
* @return header值
|
||||
*/
|
||||
default String header(final Header header) {
|
||||
return header(header.name());
|
||||
return header(header.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,11 +1,10 @@
|
||||
package cn.hutool.http.client;
|
||||
|
||||
import cn.hutool.core.io.stream.FastByteArrayOutputStream;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.io.stream.FastByteArrayOutputStream;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.http.HttpException;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.http.client.engine.ClientEngineFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
@ -44,6 +43,29 @@ public class HttpDownloader {
|
||||
return requestDownload(url, -1).bodyBytes();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFile(final String url, final File targetFileOrDir) {
|
||||
return downloadFile(url, targetFileOrDir, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadFile(final String url, final File targetFileOrDir, final int timeout) {
|
||||
return downloadFile(url, targetFileOrDir, timeout, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
@ -74,19 +96,6 @@ public class HttpDownloader {
|
||||
return requestDownload(url, timeout).body().write(targetFileOrDir, tempFileSuffix, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件,返回文件
|
||||
*
|
||||
* @param url 请求的url
|
||||
* @param targetFileOrDir 目标文件或目录,当为目录时,取URL中的文件名,取不到使用编码后的URL做为文件名
|
||||
* @param timeout 超时,单位毫秒,-1表示默认超时
|
||||
* @param streamProgress 进度条
|
||||
* @return 文件
|
||||
*/
|
||||
public static File downloadForFile(final String url, final File targetFileOrDir, final int timeout, final StreamProgress streamProgress) {
|
||||
return requestDownload(url, timeout).body().write(targetFileOrDir, streamProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载远程文件
|
||||
*
|
||||
@ -110,12 +119,12 @@ public class HttpDownloader {
|
||||
* @return HttpResponse
|
||||
* @since 5.4.1
|
||||
*/
|
||||
private static HttpResponse requestDownload(final String url, final int timeout) {
|
||||
private static Response requestDownload(final String url, final int timeout) {
|
||||
Assert.notBlank(url, "[url] is blank !");
|
||||
|
||||
final HttpResponse response = HttpUtil.createGet(url, true)
|
||||
.timeout(timeout)
|
||||
.executeAsync();
|
||||
final Response response = ClientEngineFactory.get()
|
||||
.setConfig(ClientConfig.of().setConnectionTimeout(timeout).setReadTimeout(timeout))
|
||||
.send(Request.of(url));
|
||||
|
||||
if (response.isOk()) {
|
||||
return response;
|
||||
|
@ -1,19 +1,23 @@
|
||||
package cn.hutool.http.client;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.GlobalHeaders;
|
||||
import cn.hutool.http.HttpGlobalConfig;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.client.body.HttpBody;
|
||||
import cn.hutool.http.client.body.StringBody;
|
||||
import cn.hutool.http.client.body.UrlEncodedFormBody;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import cn.hutool.http.meta.Method;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -96,6 +100,9 @@ public class Request implements HeaderOperation<Request> {
|
||||
method = Method.GET;
|
||||
headers = new HashMap<>();
|
||||
maxRedirectCount = HttpGlobalConfig.getMaxRedirectCount();
|
||||
|
||||
// 全局默认请求头
|
||||
header(GlobalHeaders.INSTANCE.headers(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,9 +205,7 @@ public class Request implements HeaderOperation<Request> {
|
||||
|
||||
final List<String> values = headers.get(name.trim());
|
||||
if (isOverride || CollUtil.isEmpty(values)) {
|
||||
final ArrayList<String> valueList = new ArrayList<>();
|
||||
valueList.add(value);
|
||||
headers.put(name.trim(), valueList);
|
||||
headers.put(name.trim(), ListUtil.of(value));
|
||||
} else {
|
||||
values.add(value.trim());
|
||||
}
|
||||
@ -216,6 +221,26 @@ public class Request implements HeaderOperation<Request> {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加请求表单内容
|
||||
*
|
||||
* @param formMap 表单内容
|
||||
* @return this
|
||||
*/
|
||||
public Request form(final Map<String, Object> formMap) {
|
||||
return body(new UrlEncodedFormBody(formMap, charset()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加字符串请求体
|
||||
*
|
||||
* @param body 请求体
|
||||
* @return this
|
||||
*/
|
||||
public Request body(final String body) {
|
||||
return body(new StringBody(body, charset()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加请求体
|
||||
*
|
||||
@ -227,7 +252,7 @@ public class Request implements HeaderOperation<Request> {
|
||||
|
||||
// 根据内容赋值默认Content-Type
|
||||
if (StrUtil.isBlank(header(Header.CONTENT_TYPE))) {
|
||||
header(Header.CONTENT_TYPE, body.getContentType(), true);
|
||||
header(Header.CONTENT_TYPE, body.getContentType(charset()), true);
|
||||
}
|
||||
|
||||
return this;
|
||||
@ -253,4 +278,13 @@ public class Request implements HeaderOperation<Request> {
|
||||
this.maxRedirectCount = Math.max(maxRedirectCount, 0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*
|
||||
* @return 响应内容
|
||||
*/
|
||||
public Response send() {
|
||||
return HttpUtil.send(this);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,9 @@ import cn.hutool.http.meta.Header;
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 响应内容接口,包括响应状态码、HTTP消息头、响应体等信息
|
||||
@ -35,12 +38,19 @@ public interface Response extends Closeable {
|
||||
*/
|
||||
String header(final String name);
|
||||
|
||||
/**
|
||||
* 获取headers
|
||||
*
|
||||
* @return Headers Map
|
||||
*/
|
||||
Map<String, List<String>> headers();
|
||||
|
||||
/**
|
||||
* 获取字符集编码,默认为响应头中的编码
|
||||
*
|
||||
* @return 字符集
|
||||
*/
|
||||
default Charset charset(){
|
||||
default Charset charset() {
|
||||
return HttpUtil.getCharset(header(Header.CONTENT_TYPE));
|
||||
}
|
||||
|
||||
@ -54,9 +64,10 @@ public interface Response extends Closeable {
|
||||
|
||||
/**
|
||||
* 获取响应体,包含服务端返回的内容和Content-Type信息
|
||||
*
|
||||
* @return {@link ResponseBody}
|
||||
*/
|
||||
default ResponseBody body(){
|
||||
default ResponseBody body() {
|
||||
return new ResponseBody(this, bodyStream(), false, true);
|
||||
}
|
||||
|
||||
@ -152,4 +163,13 @@ public interface Response extends Closeable {
|
||||
default String getCookieStr() {
|
||||
return header(Header.SET_COOKIE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 链式处理结果
|
||||
*
|
||||
* @param consumer {@link Consumer}
|
||||
*/
|
||||
default void then(final Consumer<Response> consumer) {
|
||||
consumer.accept(this);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* 定义请求体接口
|
||||
@ -25,6 +26,20 @@ public interface HttpBody {
|
||||
*/
|
||||
String getContentType();
|
||||
|
||||
/**
|
||||
* 获取指定编码的Content-Type,类似于:application/json;charset=UTF-8
|
||||
* @param charset 编码
|
||||
* @return Content-Type
|
||||
*/
|
||||
default String getContentType(final Charset charset){
|
||||
final String contentType = getContentType();
|
||||
if(null == contentType){
|
||||
return null;
|
||||
}
|
||||
|
||||
return contentType + ";charset=" + charset.name();
|
||||
}
|
||||
|
||||
/**
|
||||
* 写出并关闭{@link OutputStream}
|
||||
*
|
||||
|
@ -25,7 +25,7 @@ public class ClientEngineFactory {
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户引入的拼音引擎jar,自动创建对应的拼音引擎对象<br>
|
||||
* 根据用户引入的HTTP客户端引擎jar,自动创建对应的拼音引擎对象<br>
|
||||
* 推荐创建的引擎单例使用,此方法每次调用会返回新的引擎
|
||||
*
|
||||
* @return {@code ClientEngine}
|
||||
|
@ -1,14 +1,18 @@
|
||||
package cn.hutool.http.client.engine.httpclient4;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.http.GlobalHeaders;
|
||||
import cn.hutool.http.HttpException;
|
||||
import cn.hutool.http.client.ClientConfig;
|
||||
import cn.hutool.http.client.ClientEngine;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.client.body.HttpBody;
|
||||
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
@ -29,20 +33,27 @@ import java.util.Map;
|
||||
*/
|
||||
public class HttpClient4Engine implements ClientEngine {
|
||||
|
||||
private final CloseableHttpClient engine;
|
||||
private ClientConfig config;
|
||||
private CloseableHttpClient engine;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public HttpClient4Engine() {
|
||||
this.engine = HttpClients.custom()
|
||||
// 设置默认头信息
|
||||
.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
|
||||
.build();
|
||||
public HttpClient4Engine() {}
|
||||
|
||||
@Override
|
||||
public HttpClient4Engine setConfig(final ClientConfig config) {
|
||||
this.config = config;
|
||||
// 重置客户端
|
||||
IoUtil.close(this.engine);
|
||||
this.engine = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response send(final Request message) {
|
||||
initEngine();
|
||||
|
||||
final HttpEntityEnclosingRequestBase request = buildRequest(message);
|
||||
final CloseableHttpResponse response;
|
||||
try {
|
||||
@ -64,6 +75,29 @@ public class HttpClient4Engine implements ClientEngine {
|
||||
this.engine.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化引擎
|
||||
*/
|
||||
private void initEngine(){
|
||||
if(null != this.engine){
|
||||
return;
|
||||
}
|
||||
|
||||
RequestConfig requestConfig = null;
|
||||
if(null != this.config){
|
||||
requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(this.config.getConnectionTimeout())
|
||||
.setConnectionRequestTimeout(this.config.getConnectionTimeout())
|
||||
.build();
|
||||
}
|
||||
|
||||
this.engine = HttpClients.custom()
|
||||
// 设置默认头信息
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求体
|
||||
*
|
||||
|
@ -13,6 +13,11 @@ import org.apache.http.util.EntityUtils;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HttpClient响应包装<br>
|
||||
@ -59,6 +64,17 @@ public class HttpClient4Response implements Response {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> headers() {
|
||||
final Header[] headers = rawRes.getAllHeaders();
|
||||
final HashMap<String, List<String>> result = new LinkedHashMap<>(headers.length, 1);
|
||||
for (final Header header : headers) {
|
||||
final List<String> valueList = result.computeIfAbsent(header.getName(), k -> new ArrayList<>());
|
||||
valueList.add(header.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return rawRes.getEntity().getContentLength();
|
||||
|
@ -1,16 +1,20 @@
|
||||
package cn.hutool.http.client.engine.httpclient5;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.http.GlobalHeaders;
|
||||
import cn.hutool.http.HttpException;
|
||||
import cn.hutool.http.client.ClientConfig;
|
||||
import cn.hutool.http.client.ClientEngine;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.client.body.HttpBody;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase;
|
||||
import org.apache.hc.client5.http.config.RequestConfig;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
|
||||
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
|
||||
import org.apache.hc.client5.http.impl.classic.HttpClients;
|
||||
import org.apache.hc.core5.http.ClassicHttpRequest;
|
||||
import org.apache.hc.core5.http.Header;
|
||||
@ -21,6 +25,7 @@ import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Apache HttpClient5的HTTP请求引擎
|
||||
@ -30,20 +35,27 @@ import java.util.Map;
|
||||
*/
|
||||
public class HttpClient5Engine implements ClientEngine {
|
||||
|
||||
private final CloseableHttpClient engine;
|
||||
private ClientConfig config;
|
||||
private CloseableHttpClient engine;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public HttpClient5Engine() {
|
||||
this.engine = HttpClients.custom()
|
||||
// 设置默认头信息
|
||||
.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()))
|
||||
.build();
|
||||
public HttpClient5Engine() {}
|
||||
|
||||
@Override
|
||||
public HttpClient5Engine setConfig(final ClientConfig config) {
|
||||
this.config = config;
|
||||
// 重置客户端
|
||||
IoUtil.close(this.engine);
|
||||
this.engine = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response send(final Request message) {
|
||||
initEngine();
|
||||
|
||||
final ClassicHttpRequest request = buildRequest(message);
|
||||
final CloseableHttpResponse response;
|
||||
try {
|
||||
@ -65,6 +77,33 @@ public class HttpClient5Engine implements ClientEngine {
|
||||
this.engine.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化引擎
|
||||
*/
|
||||
private void initEngine(){
|
||||
if(null != this.engine){
|
||||
return;
|
||||
}
|
||||
|
||||
RequestConfig requestConfig = null;
|
||||
if(null != this.config){
|
||||
requestConfig = RequestConfig.custom()
|
||||
.setConnectTimeout(this.config.getConnectionTimeout(), TimeUnit.MILLISECONDS)
|
||||
.setConnectionRequestTimeout(this.config.getConnectionTimeout(), TimeUnit.MILLISECONDS)
|
||||
.setResponseTimeout(this.config.getReadTimeout(), TimeUnit.MILLISECONDS)
|
||||
.build();
|
||||
}
|
||||
|
||||
final HttpClientBuilder builder = HttpClients.custom()
|
||||
// 设置默认头信息
|
||||
.setDefaultRequestConfig(requestConfig)
|
||||
.setDefaultHeaders(toHeaderList(GlobalHeaders.INSTANCE.headers()));
|
||||
|
||||
// TODO 设置代理
|
||||
|
||||
this.engine = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求体
|
||||
*
|
||||
|
@ -13,6 +13,11 @@ import org.apache.hc.core5.http.io.entity.EntityUtils;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* HttpClient响应包装<br>
|
||||
@ -59,6 +64,17 @@ public class HttpClient5Response implements Response {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> headers() {
|
||||
final Header[] headers = rawRes.getHeaders();
|
||||
final HashMap<String, List<String>> result = new LinkedHashMap<>(headers.length, 1);
|
||||
for (final Header header : headers) {
|
||||
final List<String> valueList = result.computeIfAbsent(header.getName(), k -> new ArrayList<>());
|
||||
valueList.add(header.getValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long contentLength() {
|
||||
return rawRes.getEntity().getContentLength();
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -95,6 +95,7 @@ public class HttpResponse implements Response, Closeable {
|
||||
*
|
||||
* @return Headers Map
|
||||
*/
|
||||
@Override
|
||||
public Map<String, List<String>> headers() {
|
||||
return Collections.unmodifiableMap(headers);
|
||||
}
|
||||
@ -234,7 +235,6 @@ public class HttpResponse implements Response, Closeable {
|
||||
*
|
||||
* @throws HttpException IO异常
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
private void init(final boolean isAsync, final boolean isIgnoreBody) throws HttpException {
|
||||
// 获取响应状态码
|
||||
try {
|
||||
|
@ -3,6 +3,7 @@ package cn.hutool.http.client.engine.jdk;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.HttpException;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.http.client.ClientConfig;
|
||||
@ -25,7 +26,7 @@ import java.net.HttpURLConnection;
|
||||
*/
|
||||
public class JdkClientEngine implements ClientEngine {
|
||||
|
||||
private final ClientConfig config;
|
||||
private ClientConfig config;
|
||||
private HttpConnection conn;
|
||||
/**
|
||||
* 重定向次数计数器,内部使用
|
||||
@ -35,13 +36,21 @@ public class JdkClientEngine implements ClientEngine {
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public JdkClientEngine() {
|
||||
this.config = ClientConfig.of();
|
||||
public JdkClientEngine() {}
|
||||
|
||||
@Override
|
||||
public JdkClientEngine setConfig(final ClientConfig config) {
|
||||
this.config = config;
|
||||
if(null != this.conn){
|
||||
this.conn.disconnectQuietly();
|
||||
this.conn = null;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response send(final Request message) {
|
||||
return send(message, false);
|
||||
return send(message, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,8 +122,10 @@ public class JdkClientEngine implements ClientEngine {
|
||||
* @return {@link HttpConnection}
|
||||
*/
|
||||
private HttpConnection buildConn(final Request message) {
|
||||
final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
|
||||
|
||||
final HttpConnection conn = HttpConnection
|
||||
.of(message.url().toURL(), config.proxy)
|
||||
.of(message.url().toURL(), config.getProxy())
|
||||
.setConnectTimeout(config.getConnectionTimeout())
|
||||
.setReadTimeout(config.getReadTimeout())
|
||||
.setMethod(message.method())//
|
||||
|
@ -1,6 +1,8 @@
|
||||
package cn.hutool.http.client.engine.okhttp;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.client.ClientConfig;
|
||||
import cn.hutool.http.client.ClientEngine;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
@ -8,6 +10,8 @@ import okhttp3.OkHttpClient;
|
||||
import okhttp3.internal.http.HttpMethod;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* OkHttp3客户端引擎封装
|
||||
@ -17,7 +21,8 @@ import java.io.IOException;
|
||||
*/
|
||||
public class OkHttpEngine implements ClientEngine {
|
||||
|
||||
private final OkHttpClient client;
|
||||
private ClientConfig config;
|
||||
private OkHttpClient client;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@ -26,8 +31,18 @@ public class OkHttpEngine implements ClientEngine {
|
||||
this.client = new OkHttpClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OkHttpEngine setConfig(final ClientConfig config) {
|
||||
this.config = config;
|
||||
// 重置客户端
|
||||
this.client = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response send(final Request message) {
|
||||
initEngine();
|
||||
|
||||
final okhttp3.Response response;
|
||||
try {
|
||||
response = client.newCall(buildRequest(message)).execute();
|
||||
@ -48,6 +63,28 @@ public class OkHttpEngine implements ClientEngine {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化引擎
|
||||
*/
|
||||
private void initEngine() {
|
||||
if (null != this.client) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
|
||||
final OkHttpClient.Builder builder = new OkHttpClient.Builder()
|
||||
.connectTimeout(config.getConnectionTimeout(), TimeUnit.MILLISECONDS)
|
||||
.readTimeout(config.getReadTimeout(), TimeUnit.MILLISECONDS);
|
||||
|
||||
// 设置代理
|
||||
final Proxy proxy = config.getProxy();
|
||||
if(null != proxy){
|
||||
builder.proxy(proxy);
|
||||
}
|
||||
|
||||
this.client = builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建请求体
|
||||
*
|
||||
@ -59,9 +96,9 @@ public class OkHttpEngine implements ClientEngine {
|
||||
.url(message.url().toURL());
|
||||
|
||||
final String method = message.method().name();
|
||||
if(HttpMethod.permitsRequestBody(method)){
|
||||
if (HttpMethod.permitsRequestBody(method)) {
|
||||
builder.method(method, new OkHttpRequestBody(message.body()));
|
||||
}else{
|
||||
} else {
|
||||
builder.method(method, null);
|
||||
}
|
||||
|
||||
|
@ -3,11 +3,18 @@ package cn.hutool.http.client.engine.okhttp;
|
||||
import cn.hutool.core.io.stream.EmptyInputStream;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.client.Response;
|
||||
import kotlin.Pair;
|
||||
import okhttp3.Headers;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* OkHttp3的{@link okhttp3.Response} 响应包装
|
||||
@ -41,6 +48,17 @@ public class OkHttpResponse implements Response {
|
||||
return rawRes.header(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> headers() {
|
||||
final Headers headers = rawRes.headers();
|
||||
final HashMap<String, List<String>> result = new LinkedHashMap<>(headers.size(), 1);
|
||||
for (final Pair<? extends String, ? extends String> header : headers) {
|
||||
final List<String> valueList = result.computeIfAbsent(header.getFirst(), k -> new ArrayList<>());
|
||||
valueList.add(header.getSecond());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Charset charset() {
|
||||
return ObjUtil.defaultIfNull(Response.super.charset(), requestCharset);
|
||||
|
@ -8,6 +8,7 @@ import cn.hutool.core.map.multi.ListValueMap;
|
||||
import cn.hutool.core.net.NetUtil;
|
||||
import cn.hutool.core.net.multipart.MultipartFormData;
|
||||
import cn.hutool.core.net.multipart.UploadSetting;
|
||||
import cn.hutool.core.net.url.UrlQueryUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
@ -329,7 +330,7 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
//解析URL中的参数
|
||||
final String query = getQuery();
|
||||
if(StrUtil.isNotBlank(query)){
|
||||
this.paramsCache.putAll(HttpUtil.decodeParams(query, charset));
|
||||
this.paramsCache.putAll(UrlQueryUtil.decodeQueryList(query, charset));
|
||||
}
|
||||
|
||||
// 解析multipart中的参数
|
||||
@ -339,7 +340,7 @@ public class HttpServerRequest extends HttpServerBase {
|
||||
// 解析body中的参数
|
||||
final String body = getBody();
|
||||
if(StrUtil.isNotBlank(body)){
|
||||
this.paramsCache.putAll(HttpUtil.decodeParams(body, charset));
|
||||
this.paramsCache.putAll(UrlQueryUtil.decodeQueryList(body, charset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,14 @@ package cn.hutool.http.webservice;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.XmlUtil;
|
||||
import cn.hutool.http.client.engine.jdk.HttpBase;
|
||||
import cn.hutool.http.HttpGlobalConfig;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.client.engine.ClientEngineFactory;
|
||||
import cn.hutool.http.client.engine.jdk.HttpBase;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.namespace.QName;
|
||||
@ -542,7 +543,7 @@ public class SoapClient extends HttpBase<SoapClient> {
|
||||
* @return 返回结果
|
||||
*/
|
||||
public SOAPMessage sendForMessage() {
|
||||
final HttpResponse res = sendForResponse();
|
||||
final Response res = sendForResponse();
|
||||
final MimeHeaders headers = new MimeHeaders();
|
||||
for (final Entry<String, List<String>> entry : res.headers().entrySet()) {
|
||||
if (StrUtil.isNotEmpty(entry.getKey())) {
|
||||
@ -585,15 +586,13 @@ public class SoapClient extends HttpBase<SoapClient> {
|
||||
*
|
||||
* @return 响应对象
|
||||
*/
|
||||
public HttpResponse sendForResponse() {
|
||||
return HttpRequest.post(this.url)//
|
||||
.setFollowRedirects(true)//
|
||||
.setConnectionTimeout(this.connectionTimeout)
|
||||
.setReadTimeout(this.readTimeout)
|
||||
.contentType(getXmlContentType())//
|
||||
.header(this.headers())
|
||||
.body(getMsgStr(false))//
|
||||
.executeAsync();
|
||||
public Response sendForResponse() {
|
||||
final Request request = Request.of(this.url)
|
||||
.setMaxRedirectCount(2)
|
||||
.contentType(getXmlContentType())
|
||||
.header(this.headers, false)
|
||||
.body(getMsgStr(false));
|
||||
return ClientEngineFactory.get().send(request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,14 +1,19 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.codec.BaseN.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.StreamProgress;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.http.client.HttpDownloader;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.engine.ClientEngineFactory;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
@ -26,7 +31,7 @@ public class DownloadTest {
|
||||
@Ignore
|
||||
public void downloadPicTest() {
|
||||
final String url = "http://wx.qlogo.cn/mmopen/vKhlFcibVUtNBVDjcIowlg0X8aJfHXrTNCEFBukWVH9ta99pfEN88lU39MKspCUCOP3yrFBH3y2NbV7sYtIIlon8XxLwAEqv2/0";
|
||||
HttpUtil.downloadFile(url, "e:/pic/t3.jpg");
|
||||
HttpDownloader.downloadFile(url, new File("e:/pic/t3.jpg"));
|
||||
Console.log("ok");
|
||||
}
|
||||
|
||||
@ -34,13 +39,14 @@ public class DownloadTest {
|
||||
@Ignore
|
||||
public void downloadSizeTest() {
|
||||
final String url = "https://res.t-io.org/im/upload/img/67/8948/1119501/88097554/74541310922/85/231910/366466 - 副本.jpg";
|
||||
HttpRequest.get(url).setSSLProtocol("TLSv1.2").executeAsync().body().write("e:/pic/366466.jpg");
|
||||
ClientEngineFactory.get().send(Request.of(url)).body().write("e:/pic/366466.jpg");
|
||||
//HttpRequest.get(url).setSSLProtocol("TLSv1.2").executeAsync().body().write("e:/pic/366466.jpg");
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadTest1() {
|
||||
final File size = HttpUtil.downloadFile("http://explorer.bbfriend.com/crossdomain.xml", "e:/temp/");
|
||||
final File size = HttpDownloader.downloadFile("http://explorer.bbfriend.com/crossdomain.xml", new File("e:/temp/"));
|
||||
System.out.println("Download size: " + size);
|
||||
}
|
||||
|
||||
@ -48,7 +54,7 @@ public class DownloadTest {
|
||||
@Ignore
|
||||
public void downloadTest() {
|
||||
// 带进度显示的文件下载
|
||||
HttpUtil.downloadFile("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso", FileUtil.file("d:/"), new StreamProgress() {
|
||||
HttpDownloader.downloadFile("http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2009.iso", FileUtil.file("d:/"), -1, new StreamProgress() {
|
||||
|
||||
final long time = System.currentTimeMillis();
|
||||
|
||||
@ -73,7 +79,7 @@ public class DownloadTest {
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadFileFromUrlTest1() {
|
||||
final File file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", "d:/download/temp");
|
||||
final File file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", new File("d:/download/temp"));
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.isFile());
|
||||
Assert.assertTrue(file.length() > 0);
|
||||
@ -84,7 +90,7 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest2() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), 1, new StreamProgress() {
|
||||
file = HttpDownloader.downloadFile("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), 1, new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("start");
|
||||
@ -118,7 +124,7 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest3() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), new StreamProgress() {
|
||||
file = HttpDownloader.downloadFile("https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.4.0/hutool-all-5.4.0-sources.jar", FileUtil.file("d:/download/temp"), -1, new StreamProgress() {
|
||||
@Override
|
||||
public void start() {
|
||||
System.out.println("start");
|
||||
@ -150,7 +156,7 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest4() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
|
||||
file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"), 1);
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.exists());
|
||||
@ -170,7 +176,7 @@ public class DownloadTest {
|
||||
public void downloadFileFromUrlTest5() {
|
||||
File file = null;
|
||||
try {
|
||||
file = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
|
||||
file = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp", UUID.randomUUID().toString()));
|
||||
|
||||
Assert.assertNotNull(file);
|
||||
Assert.assertTrue(file.exists());
|
||||
@ -182,7 +188,7 @@ public class DownloadTest {
|
||||
|
||||
File file1 = null;
|
||||
try {
|
||||
file1 = HttpUtil.downloadFileFromUrl("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
|
||||
file1 = HttpDownloader.downloadFile("http://groovy-lang.org/changelogs/changelog-3.0.5.html", FileUtil.file("d:/download/temp"));
|
||||
|
||||
Assert.assertNotNull(file1);
|
||||
Assert.assertTrue(file1.exists());
|
||||
@ -200,7 +206,34 @@ public class DownloadTest {
|
||||
final String url = "https://download.teamviewer.com/download/TeamViewer_Setup_x64.exe";
|
||||
HttpGlobalConfig.setMaxRedirectCount(20);
|
||||
final Path temp = Files.createTempFile("tmp", ".exe");
|
||||
final File file = HttpUtil.downloadFileFromUrl(url, temp.toFile());
|
||||
final File file = HttpDownloader.downloadFile(url, temp.toFile());
|
||||
Console.log(file.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadToStreamTest() {
|
||||
String url2 = "http://storage.chancecloud.com.cn/20200413_%E7%B2%A4B12313_386.pdf";
|
||||
final ByteArrayOutputStream os2 = new ByteArrayOutputStream();
|
||||
HttpDownloader.download(url2, os2, false, null);
|
||||
|
||||
url2 = "http://storage.chancecloud.com.cn/20200413_粤B12313_386.pdf";
|
||||
HttpDownloader.download(url2, os2, false, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadStringTest() {
|
||||
final String url = "https://www.baidu.com";
|
||||
// 从远程直接读取字符串,需要自定义编码,直接调用JDK方法
|
||||
final String content2 = HttpDownloader.downloadString(url, CharsetUtil.UTF_8, null);
|
||||
Console.log(content2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void gimg2Test(){
|
||||
final byte[] bytes = HttpDownloader.downloadBytes("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea");
|
||||
Console.log(Base64.encode(bytes));
|
||||
}
|
||||
}
|
||||
|
@ -4,11 +4,11 @@ import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.StopWatch;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.net.ssl.SSLProtocols;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import cn.hutool.http.meta.Method;
|
||||
import org.junit.Ignore;
|
||||
@ -20,7 +20,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* {@link HttpRequest}单元测试
|
||||
* {@link Request}单元测试
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
@ -28,18 +28,11 @@ import java.util.Map;
|
||||
public class HttpRequestTest {
|
||||
final String url = "http://photo.qzone.qq.com/fcgi-bin/fcg_list_album?uin=88888&outstyle=2";
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getHttpsTest() {
|
||||
final String body = HttpRequest.get("https://www.hutool.cn/").timeout(10).execute().bodyStr();
|
||||
Console.log(body);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getHttpsThenTest() {
|
||||
HttpRequest
|
||||
.get("https://hutool.cn")
|
||||
Request.of("https://hutool.cn")
|
||||
.send()
|
||||
.then(response -> Console.log(response.body()));
|
||||
}
|
||||
|
||||
@ -47,9 +40,9 @@ public class HttpRequestTest {
|
||||
@Ignore
|
||||
public void getCookiesTest() {
|
||||
// 检查在Connection关闭情况下Cookie是否可以正常获取
|
||||
final HttpResponse res = HttpRequest.get("https://www.oschina.net/").execute();
|
||||
final Response res = Request.of("https://www.oschina.net/").send();
|
||||
final String body = res.bodyStr();
|
||||
Console.log(res.getCookies());
|
||||
Console.log(res.getCookieStr());
|
||||
Console.log(body);
|
||||
}
|
||||
|
||||
@ -58,14 +51,14 @@ public class HttpRequestTest {
|
||||
public void toStringTest() {
|
||||
final String url = "http://gc.ditu.aliyun.com/geocoding?ccc=你好";
|
||||
|
||||
final HttpRequest request = HttpRequest.get(url).body("a=乌海");
|
||||
final Request request = Request.of(url).body("a=乌海");
|
||||
Console.log(request.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void asyncHeadTest() {
|
||||
final HttpResponse response = HttpRequest.head(url).execute();
|
||||
final Response response = Request.of(url).method(Method.HEAD).send();
|
||||
final Map<String, List<String>> headers = response.headers();
|
||||
Console.log(headers);
|
||||
Console.log(response.body());
|
||||
@ -76,7 +69,7 @@ public class HttpRequestTest {
|
||||
public void asyncGetTest() {
|
||||
final StopWatch timer = DateUtil.createStopWatch();
|
||||
timer.start();
|
||||
final HttpResponse body = HttpRequest.get(url).charset("GBK").executeAsync();
|
||||
final Response body = Request.of(url).charset(CharsetUtil.GBK).send();
|
||||
timer.stop();
|
||||
final long interval = timer.getLastTaskTimeMillis();
|
||||
timer.start();
|
||||
@ -91,7 +84,7 @@ public class HttpRequestTest {
|
||||
public void syncGetTest() {
|
||||
final StopWatch timer = DateUtil.createStopWatch();
|
||||
timer.start();
|
||||
final HttpResponse body = HttpRequest.get(url).charset("GBK").execute();
|
||||
final Response body = Request.of(url).charset(CharsetUtil.GBK).send();
|
||||
timer.stop();
|
||||
final long interval = timer.getLastTaskTimeMillis();
|
||||
|
||||
@ -102,26 +95,12 @@ public class HttpRequestTest {
|
||||
Console.log("Async response spend {}ms, body spend {}ms", interval, interval2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void customGetTest() {
|
||||
// 自定义构建HTTP GET请求,发送Http GET请求,针对HTTPS安全加密,可以自定义SSL
|
||||
final HttpRequest request = HttpRequest.get(url)
|
||||
// 自定义返回编码
|
||||
.charset(CharsetUtil.GBK)
|
||||
// 禁用缓存
|
||||
.disableCache()
|
||||
// 自定义SSL版本
|
||||
.setSSLProtocol(SSLProtocols.TLSv12);
|
||||
Console.log(request.execute().body());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getDeflateTest() {
|
||||
final HttpResponse res = HttpRequest.get("https://comment.bilibili.com/67573272.xml")
|
||||
final Response res = Request.of("https://comment.bilibili.com/67573272.xml")
|
||||
.header(Header.ACCEPT_ENCODING, "deflate")
|
||||
.execute();
|
||||
.send();
|
||||
Console.log(res.header(Header.CONTENT_ENCODING));
|
||||
Console.log(res.body());
|
||||
}
|
||||
@ -129,7 +108,7 @@ public class HttpRequestTest {
|
||||
@Test
|
||||
@Ignore
|
||||
public void bodyTest() {
|
||||
final String ddddd1 = HttpRequest.get("https://baijiahao.baidu.com/s").body("id=1625528941695652600").execute().bodyStr();
|
||||
final String ddddd1 = Request.of("https://baijiahao.baidu.com/s").body("id=1625528941695652600").send().bodyStr();
|
||||
Console.log(ddddd1);
|
||||
}
|
||||
|
||||
@ -149,9 +128,9 @@ public class HttpRequestTest {
|
||||
map.put("size", "2");
|
||||
map.put("sizes", list);
|
||||
|
||||
HttpRequest
|
||||
.get("http://localhost:8888/get")
|
||||
.form(map)
|
||||
Request
|
||||
.of("http://localhost:8888/get")
|
||||
.form(map).send()
|
||||
.then(resp -> Console.log(resp.body()));
|
||||
}
|
||||
|
||||
@ -159,9 +138,9 @@ public class HttpRequestTest {
|
||||
@Ignore
|
||||
public void getWithoutEncodeTest() {
|
||||
final String url = "https://img-cloud.voc.com.cn/140/2020/09/03/c3d41b93e0d32138574af8e8b50928b376ca5ba61599127028157.png?imageMogr2/auto-orient/thumbnail/500&pid=259848";
|
||||
final HttpRequest get = HttpUtil.createGet(url);
|
||||
Console.log(get.getUrl());
|
||||
final HttpResponse execute = get.execute();
|
||||
final Request get = Request.of(url);
|
||||
Console.log(get.url());
|
||||
final Response execute = get.send();
|
||||
Console.log(execute.body());
|
||||
}
|
||||
|
||||
@ -176,11 +155,11 @@ public class HttpRequestTest {
|
||||
|
||||
// 方式1:全局设置
|
||||
HttpGlobalConfig.setMaxRedirectCount(1);
|
||||
HttpResponse execute = HttpRequest.get(url).execute();
|
||||
Response execute = Request.of(url).send();
|
||||
Console.log(execute.getStatus(), execute.header(Header.LOCATION));
|
||||
|
||||
// 方式2,单独设置
|
||||
execute = HttpRequest.get(url).setMaxRedirectCount(1).execute();
|
||||
execute = Request.of(url).setMaxRedirectCount(1).send();
|
||||
Console.log(execute.getStatus(), execute.header(Header.LOCATION));
|
||||
}
|
||||
|
||||
@ -190,8 +169,8 @@ public class HttpRequestTest {
|
||||
final String url = "https://postman-echo.com/get";
|
||||
final Map<String, Object> map = new HashMap<>();
|
||||
map.put("aaa", "application+1@qqq.com");
|
||||
final HttpRequest request =HttpUtil.createGet(url).form(map);
|
||||
Console.log(request.execute().body());
|
||||
final Request request =Request.of(url).form(map);
|
||||
Console.log(request.send().body());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -200,51 +179,51 @@ public class HttpRequestTest {
|
||||
final UrlBuilder urlBuilder = new UrlBuilder();
|
||||
urlBuilder.setScheme("https").setHost("hutool.cn");
|
||||
|
||||
final HttpRequest httpRequest = new HttpRequest(urlBuilder);
|
||||
httpRequest.method(Method.GET).execute();
|
||||
final Request httpRequest = Request.of(urlBuilder);
|
||||
httpRequest.method(Method.GET).send();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getCookieTest(){
|
||||
final HttpResponse execute = HttpRequest.get("http://localhost:8888/getCookier").execute();
|
||||
Console.log(execute.getCookies());
|
||||
final Response execute = Request.of("http://localhost:8888/getCookier").send();
|
||||
Console.log(execute.getCookieStr());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void optionsTest() {
|
||||
final HttpRequest options = HttpRequest.options("https://hutool.cn");
|
||||
final Request options = Request.of("https://hutool.cn").method(Method.OPTIONS);
|
||||
Assert.notNull(options.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deleteTest() {
|
||||
final HttpRequest options = HttpRequest.delete("https://hutool.cn");
|
||||
final Request options = Request.of("https://hutool.cn").method(Method.DELETE);
|
||||
Assert.notNull(options.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void traceTest() {
|
||||
final HttpRequest options = HttpRequest.trace("https://hutool.cn");
|
||||
final Request options = Request.of("https://hutool.cn").method(Method.TRACE);
|
||||
Assert.notNull(options.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getToStringTest() {
|
||||
final HttpRequest a = HttpRequest.get("https://hutool.cn/").form("a", 1);
|
||||
final Request a = Request.of("https://hutool.cn/").form(MapUtil.of("a", 1));
|
||||
Assert.notNull(a.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postToStringTest() {
|
||||
final HttpRequest a = HttpRequest.post("https://hutool.cn/").form("a", 1);
|
||||
final Request a = Request.of("https://hutool.cn/").method(Method.POST).form(MapUtil.of("a", 1));
|
||||
Console.log(a.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void issueI5Y68WTest() {
|
||||
final HttpResponse httpResponse = HttpRequest.get("http://82.157.17.173:8100/app/getAddress").execute();
|
||||
final Response httpResponse = Request.of("http://82.157.17.173:8100/app/getAddress").send();
|
||||
Console.log(httpResponse.body());
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,16 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.codec.BaseN.Base64;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.regex.ReUtil;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import cn.hutool.http.meta.Method;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -35,23 +33,13 @@ public class HttpUtilTest {
|
||||
Assert.assertFalse(HttpUtil.isHttps("ftp://aaa.bbb"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void postTest() {
|
||||
final String result = HttpUtil.createPost("api.uhaozu.com/goods/description/1120448506")
|
||||
.charset(CharsetUtil.NAME_UTF_8)
|
||||
.execute().bodyStr();
|
||||
Console.log(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void postTest2() {
|
||||
// 某些接口对Accept头有特殊要求,此处自定义头
|
||||
final String result = HttpUtil
|
||||
.createPost("http://cmp.ishanghome.com/cmp/v1/community/queryClusterCommunity")
|
||||
.header(Header.ACCEPT, "*/*")
|
||||
.execute()
|
||||
final String result = HttpUtil.send(Request
|
||||
.of("http://cmp.ishanghome.com/cmp/v1/community/queryClusterCommunity")
|
||||
.header(Header.ACCEPT, "*/*"))
|
||||
.bodyStr();
|
||||
Console.log(result);
|
||||
}
|
||||
@ -68,9 +56,9 @@ public class HttpUtilTest {
|
||||
public void getTest2() {
|
||||
// 此链接较为特殊,User-Agent去掉后进入一个JS跳转页面,如果设置了,需要开启302跳转
|
||||
// 自定义的默认header无效
|
||||
final String result = HttpRequest
|
||||
.get("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101457313&redirect_uri=http%3A%2F%2Fwww.benmovip.com%2Fpay-cloud%2Fqqlogin%2FgetCode&state=ok")
|
||||
.removeHeader(Header.USER_AGENT).execute().bodyStr();
|
||||
final String result = Request
|
||||
.of("https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=101457313&redirect_uri=http%3A%2F%2Fwww.benmovip.com%2Fpay-cloud%2Fqqlogin%2FgetCode&state=ok")
|
||||
.header(Header.USER_AGENT, null).send().bodyStr();
|
||||
Console.log(result);
|
||||
}
|
||||
|
||||
@ -86,38 +74,17 @@ public class HttpUtilTest {
|
||||
@Ignore
|
||||
public void getTest4() {
|
||||
// 测试url中带有空格的情况
|
||||
final byte[] str = HttpRequest.get("http://img01.fs.yiban.cn/mobile/2D0Y71").execute().bodyBytes();
|
||||
final byte[] str = Request.of("http://img01.fs.yiban.cn/mobile/2D0Y71").send().bodyBytes();
|
||||
|
||||
FileUtil.writeBytes(str, "f:/test/2D.jpg");
|
||||
Console.log(str);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getTest5() {
|
||||
String url2 = "http://storage.chancecloud.com.cn/20200413_%E7%B2%A4B12313_386.pdf";
|
||||
final ByteArrayOutputStream os2 = new ByteArrayOutputStream();
|
||||
HttpUtil.download(url2, os2, false);
|
||||
|
||||
url2 = "http://storage.chancecloud.com.cn/20200413_粤B12313_386.pdf";
|
||||
HttpUtil.download(url2, os2, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void get12306Test() {
|
||||
HttpRequest.get("https://kyfw.12306.cn/otn/")
|
||||
.setFollowRedirects(true)
|
||||
.then(response -> Console.log(response.body()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void downloadStringTest() {
|
||||
final String url = "https://www.baidu.com";
|
||||
// 从远程直接读取字符串,需要自定义编码,直接调用JDK方法
|
||||
final String content2 = HttpUtil.downloadString(url, CharsetUtil.NAME_UTF_8);
|
||||
Console.log(content2);
|
||||
HttpUtil.send(Request.of("https://kyfw.12306.cn/otn/").setMaxRedirectCount(2))
|
||||
.then(response -> Console.log(response.bodyStr()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -138,141 +105,10 @@ public class HttpUtilTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeParamsTest() {
|
||||
final String paramsStr = "uuuu=0&a=b&c=%3F%23%40!%24%25%5E%26%3Ddsssss555555";
|
||||
final Map<String, List<String>> map = HttpUtil.decodeParams(paramsStr, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("0", map.get("uuuu").get(0));
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("?#@!$%^&=dsssss555555", map.get("c").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeParamMapTest() {
|
||||
// 参数值存在分界标记等号时
|
||||
final Map<String, String> paramMap = HttpUtil.decodeParamMap("https://www.xxx.com/api.action?aa=123&f_token=NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=", CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("123",paramMap.get("aa"));
|
||||
Assert.assertEquals("NzBkMjQxNDM1MDVlMDliZTk1OTU3ZDI1OTI0NTBiOWQ=",paramMap.get("f_token"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toParamsTest() {
|
||||
final String paramsStr = "uuuu=0&a=b&c=3Ddsssss555555";
|
||||
final Map<String, List<String>> map = HttpUtil.decodeParams(paramsStr, CharsetUtil.NAME_UTF_8);
|
||||
|
||||
final String encodedParams = HttpUtil.toParams(map);
|
||||
Assert.assertEquals(paramsStr, encodedParams);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void encodeParamTest() {
|
||||
// ?单独存在去除之,&单位位于末尾去除之
|
||||
String paramsStr = "?a=b&c=d&";
|
||||
String encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=b&c=d", encode);
|
||||
|
||||
// url不参与转码
|
||||
paramsStr = "http://www.abc.dd?a=b&c=d&";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("http://www.abc.dd?a=b&c=d", encode);
|
||||
|
||||
// b=b中的=被当作值的一部分,不做encode
|
||||
paramsStr = "a=b=b&c=d&";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=b=b&c=d", encode);
|
||||
|
||||
// =d的情况被处理为key为空
|
||||
paramsStr = "a=bbb&c=d&=d";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&=d", encode);
|
||||
|
||||
// d=的情况被处理为value为空
|
||||
paramsStr = "a=bbb&c=d&d=";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&d=", encode);
|
||||
|
||||
// 多个&&被处理为单个,相当于空条件
|
||||
paramsStr = "a=bbb&c=d&&&d=";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&d=", encode);
|
||||
|
||||
// &d&相当于只有键,无值得情况
|
||||
paramsStr = "a=bbb&c=d&d&";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=d&d=", encode);
|
||||
|
||||
// 中文的键和值被编码
|
||||
paramsStr = "a=bbb&c=你好&哈喽&";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=", encode);
|
||||
|
||||
// URL原样输出
|
||||
paramsStr = "https://www.hutool.cn/";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals(paramsStr, encode);
|
||||
|
||||
// URL原样输出
|
||||
paramsStr = "https://www.hutool.cn/?";
|
||||
encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("https://www.hutool.cn/", encode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decodeParamTest() {
|
||||
// 开头的?被去除
|
||||
String a = "?a=b&c=d&";
|
||||
Map<String, List<String>> map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
|
||||
// =e被当作空为key,e为value
|
||||
a = "?a=b&c=d&=e";
|
||||
map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("e", map.get("").get(0));
|
||||
|
||||
// 多余的&去除
|
||||
a = "?a=b&c=d&=e&&&&";
|
||||
map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("e", map.get("").get(0));
|
||||
|
||||
// 值为空
|
||||
a = "?a=b&c=d&e=";
|
||||
map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("", map.get("e").get(0));
|
||||
|
||||
// &=被作为键和值都为空
|
||||
a = "a=b&c=d&=";
|
||||
map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertEquals("", map.get("").get(0));
|
||||
|
||||
// &e&这类单独的字符串被当作key
|
||||
a = "a=b&c=d&e&";
|
||||
map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("b", map.get("a").get(0));
|
||||
Assert.assertEquals("d", map.get("c").get(0));
|
||||
Assert.assertNull(map.get("e").get(0));
|
||||
Assert.assertNull(map.get("").get(0));
|
||||
|
||||
// 被编码的键和值被还原
|
||||
a = "a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=";
|
||||
map = HttpUtil.decodeParams(a, CharsetUtil.NAME_UTF_8);
|
||||
Assert.assertEquals("bbb", map.get("a").get(0));
|
||||
Assert.assertEquals("你好", map.get("c").get(0));
|
||||
Assert.assertEquals("", map.get("哈喽").get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
//@Ignore
|
||||
public void patchTest() {
|
||||
// 验证patch请求是否可用
|
||||
final String body = HttpRequest.patch("https://www.baidu.com").execute().bodyStr();
|
||||
final String body = HttpUtil.send(Request.of("https://hutool.cn").method(Method.PATCH)).bodyStr();
|
||||
Console.log(body);
|
||||
}
|
||||
|
||||
@ -315,18 +151,6 @@ public class HttpUtilTest {
|
||||
Assert.assertEquals("utf-8", charsetName);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalizeParamsTest() {
|
||||
final String encodeResult = HttpUtil.normalizeParams("参数", CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("%E5%8F%82%E6%95%B0", encodeResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalizeBlankParamsTest() {
|
||||
final String encodeResult = HttpUtil.normalizeParams("", CharsetUtil.UTF_8);
|
||||
Assert.assertEquals("", encodeResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getMimeTypeTest() {
|
||||
final String mimeType = HttpUtil.getMimeType("aaa.aaa");
|
||||
@ -357,18 +181,10 @@ public class HttpUtilTest {
|
||||
Console.log(s);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void gimg2Test(){
|
||||
final byte[] bytes = HttpUtil.downloadBytes("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fpic.jj20.com%2Fup%2Fallimg%2F1114%2F0H320120Z3%2F200H3120Z3-6-1200.jpg&refer=http%3A%2F%2Fpic.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1621996490&t=8c384c2823ea453da15a1b9cd5183eea");
|
||||
Console.log(Base64.encode(bytes));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void acplayTest(){
|
||||
final String body = HttpRequest.get("https://api.acplay.net/api/v2/bangumi/9541")
|
||||
.execute().bodyStr();
|
||||
final String body = HttpUtil.send(Request.of("https://api.acplay.net/api/v2/bangumi/9541")).bodyStr();
|
||||
Console.log(body);
|
||||
}
|
||||
|
||||
@ -378,7 +194,7 @@ public class HttpUtilTest {
|
||||
HttpGlobalConfig.setDecodeUrl(false);
|
||||
final String url = "https://p3-sign.douyinpic.com/tos-cn-i-0813/f41afb2e79a94dcf80970affb9a69415~noop.webp?x-expires=1647738000&x-signature=%2Br1ekUCGjXiu50Y%2Bk0MO4ovulK8%3D&from=4257465056&s=PackSourceEnum_DOUYIN_REFLOW&se=false&sh=&sc=&l=2022021809224601020810013524310DD3&biz_tag=aweme_images";
|
||||
|
||||
final HttpRequest request = HttpRequest.of(url).method(Method.GET);
|
||||
Console.log(request.execute().body());
|
||||
final String body = HttpUtil.send(Request.of(url)).bodyStr();
|
||||
Console.log(body);
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ package cn.hutool.http;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@ -23,10 +23,10 @@ public class Issue2531Test {
|
||||
final String queryParam = MapUtil.join(map, "&", "=");//返回str=+123
|
||||
Console.log(queryParam);
|
||||
|
||||
final HttpRequest request = HttpUtil.createGet("http://localhost:8888/formTest?" + queryParam);
|
||||
final Request request = Request.of("http://localhost:8888/formTest?" + queryParam);
|
||||
//request.setUrl("http://localhost:8888/formTest" + "?" + queryParam);
|
||||
//noinspection resource
|
||||
final HttpResponse execute = request.execute();
|
||||
final Response execute = request.send();
|
||||
Console.log(execute.body());
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@ -12,10 +13,10 @@ public class IssueI5TPSYTest {
|
||||
@Ignore
|
||||
public void redirectTest() {
|
||||
final String url = "https://bsxt.gdzwfw.gov.cn/UnifiedReporting/auth/newIndex";
|
||||
final HttpResponse res = HttpUtil.createGet(url).setFollowRedirects(true)
|
||||
final Response res = HttpUtil.send(Request.of(url)
|
||||
.setMaxRedirectCount(2)
|
||||
.header(Header.USER_AGENT, "PostmanRuntime/7.29.2")
|
||||
.cookie("jsessionid=s%3ANq6YTcIHQWrHkEqOSxiQNijDMhoFNV4_.h2MVD1CkW7sOZ60OSnPs7m4K%2FhENfYy%2FdzjKvSiZF4E")
|
||||
.execute();
|
||||
.cookie("jsessionid=s%3ANq6YTcIHQWrHkEqOSxiQNijDMhoFNV4_.h2MVD1CkW7sOZ60OSnPs7m4K%2FhENfYy%2FdzjKvSiZF4E"));
|
||||
Console.log(res.body());
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
@ -19,7 +19,8 @@ public class IssueI5WAV4Test {
|
||||
map.put("flightID", 2879);
|
||||
|
||||
|
||||
final String body = HttpRequest.get("http://localhost:8884/api/test/testHttpUtilGetWithBody").body(JSONUtil.toJsonStr(map)).execute().bodyStr();
|
||||
@SuppressWarnings("resource")
|
||||
final String body = Request.of("http://localhost:8884/api/test/testHttpUtilGetWithBody").body(JSONUtil.toJsonStr(map)).send().bodyStr();
|
||||
System.out.println("使用hutool返回结果:" + body);
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import org.brotli.dec.BrotliInputStream;
|
||||
import org.junit.Ignore;
|
||||
@ -14,9 +15,10 @@ public class IssueI5XBCFTest {
|
||||
public void getTest() {
|
||||
GlobalCompressStreamRegister.INSTANCE.register("br", BrotliInputStream.class);
|
||||
|
||||
@SuppressWarnings("resource") final HttpResponse s = HttpUtil.createGet("https://static-exp1.licdn.com/sc/h/br/1cp0oqz322bdprj3qd4pojqix")
|
||||
@SuppressWarnings("resource")
|
||||
final Response s = Request.of("https://static-exp1.licdn.com/sc/h/br/1cp0oqz322bdprj3qd4pojqix")
|
||||
.header(Header.ACCEPT_ENCODING, "br")
|
||||
.execute();
|
||||
.send();
|
||||
Console.log(s.body());
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import cn.hutool.http.meta.Method;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
@ -18,21 +19,24 @@ public class RestTest {
|
||||
|
||||
@Test
|
||||
public void contentTypeTest() {
|
||||
final HttpRequest request = HttpRequest.post("http://localhost:8090/rest/restTest/")//
|
||||
final Request request = Request.of("http://localhost:8090/rest/restTest/")
|
||||
.method(Method.POST)
|
||||
.body(JSONUtil.ofObj()
|
||||
.set("aaa", "aaaValue")
|
||||
.set("键2", "值2").toString());
|
||||
Assert.assertEquals("application/json;charset=UTF-8", request.header(Header.CONTENT_TYPE));
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Test
|
||||
@Ignore
|
||||
public void postTest() {
|
||||
final HttpRequest request = HttpRequest.post("http://localhost:8090/rest/restTest/")//
|
||||
final Request request = Request.of("http://localhost:8090/rest/restTest/")
|
||||
.method(Method.POST)
|
||||
.body(JSONUtil.ofObj()
|
||||
.set("aaa", "aaaValue")
|
||||
.set("键2", "值2").toString());
|
||||
Console.log(request.execute().body());
|
||||
Console.log(request.send().body());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -47,25 +51,12 @@ public class RestTest {
|
||||
@Test
|
||||
@Ignore
|
||||
public void getWithBodyTest() {
|
||||
final HttpRequest request = HttpRequest.get("http://localhost:8888/restTest")//
|
||||
final Request request = Request.of("http://localhost:8888/restTest")//
|
||||
.header(Header.CONTENT_TYPE, "application/json")
|
||||
.body(JSONUtil.ofObj()
|
||||
.set("aaa", "aaaValue")
|
||||
.set("键2", "值2").toString());
|
||||
Console.log(request.execute().body());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getWithBodyTest2() {
|
||||
final HttpRequest request = HttpRequest.get("https://ad.oceanengine.com/open_api/2/advertiser/info/")//
|
||||
// Charles代理
|
||||
.setHttpProxy("localhost", 8888)
|
||||
.header("Access-Token","")
|
||||
.body(JSONUtil.ofObj()
|
||||
.set("advertiser_ids", new Long[] {1690657248243790L})
|
||||
.set("fields", new String[] {"id", "name", "status"}).toString());
|
||||
Console.log(request);
|
||||
Console.log(request.execute().body());
|
||||
//noinspection resource
|
||||
Console.log(request.send().body());
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,17 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.resource.MultiFileResource;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.http.client.engine.jdk.HttpRequest;
|
||||
import cn.hutool.http.client.engine.jdk.HttpResponse;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.http.client.Request;
|
||||
import cn.hutool.http.client.Response;
|
||||
import cn.hutool.http.meta.Header;
|
||||
import cn.hutool.http.meta.Method;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -30,12 +31,18 @@ public class UploadTest {
|
||||
final File file = FileUtil.file("d:\\图片1.JPG");
|
||||
final File file2 = FileUtil.file("d:\\图片3.png");
|
||||
|
||||
final Map<String, Object> form = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("file", new MultiFileResource(file2, file))
|
||||
.put("fileType", "图片")
|
||||
.build();
|
||||
|
||||
// 方法一:自定义构建表单
|
||||
final HttpRequest request = HttpRequest//
|
||||
.post("http://localhost:8888/file")//
|
||||
.form("file", file2, file)//
|
||||
.form("fileType", "图片");
|
||||
final HttpResponse response = request.execute();
|
||||
final Request request = Request//
|
||||
.of("http://localhost:8888/file")//
|
||||
.method(Method.POST)
|
||||
.form(form);
|
||||
//noinspection resource
|
||||
final Response response = request.send();
|
||||
Console.log(response.body());
|
||||
}
|
||||
|
||||
@ -52,26 +59,6 @@ public class UploadTest {
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void uploadTest2() {
|
||||
//客户端
|
||||
final String url = "http://192.168.1.200:8888/meta/upload/img";
|
||||
final Path file = Paths.get("D:\\test\\testBigData_upload.xlsx");
|
||||
final Map<String, String> headers = new HashMap<>(16);
|
||||
headers.put("md5", "aaaaaaaa");
|
||||
|
||||
final Map<String, Object> params = new HashMap<>(16);
|
||||
params.put("fileName", file.toFile().getName());
|
||||
params.put("file", file.toFile());
|
||||
final HttpRequest httpRequest = HttpRequest.post(url)
|
||||
.setChunkedStreamingMode(1024 * 1024)
|
||||
.headerMap(headers, false)
|
||||
.form(params);
|
||||
final HttpResponse httpResponse = httpRequest.execute();
|
||||
Console.log(httpResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void smmsTest(){
|
||||
@ -79,11 +66,13 @@ public class UploadTest {
|
||||
// hutool的user agent 被封了
|
||||
final String token = "test";
|
||||
final String url = "https://sm.ms/api/v2/upload";
|
||||
final String result = HttpUtil.createPost(url)
|
||||
//noinspection resource
|
||||
final String result = Request.of(url)
|
||||
.method(Method.POST)
|
||||
.header(Header.USER_AGENT, "PostmanRuntime/7.28.4")
|
||||
.auth(token)
|
||||
.form("smfile", FileUtil.file("d:/test/qrcodeCustom.png"))
|
||||
.execute().bodyStr();
|
||||
.form(MapUtil.of("smfile", FileUtil.file("d:/test/qrcodeCustom.png")))
|
||||
.send().bodyStr();
|
||||
|
||||
Console.log(result);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user