fix UrlBuilder bug

This commit is contained in:
Looly
2021-01-21 12:36:05 +08:00
parent 856e7a3e13
commit b7ca34d0e8
6 changed files with 74 additions and 26 deletions

View File

@@ -3,7 +3,7 @@
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------
# 5.5.8 (2021-01-20) # 5.5.8 (2021-01-21)
### 新特性 ### 新特性
* 【extra 】 增加自动装配SpringUtil类pr#1366@Github * 【extra 】 增加自动装配SpringUtil类pr#1366@Github
@@ -14,6 +14,8 @@
### Bug修复 ### Bug修复
* 【core 】 修复FileUtil.move以及PathUtil.copy等无法自动创建父目录的问题issue#I2CKTI@Gitee * 【core 】 修复FileUtil.move以及PathUtil.copy等无法自动创建父目录的问题issue#I2CKTI@Gitee
* 【core 】 修复Console.input读取不全问题pr#263@Gitee * 【core 】 修复Console.input读取不全问题pr#263@Gitee
* 【core 】 修复URLUtil.encodeAll未检查空指针问题issue#I2CNPS@Gitee
* 【core 】 修复UrlBuilder.of的query中含有?丢失问题issue#I2CNPS@Gitee
------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------

View File

@@ -16,16 +16,16 @@ import java.util.Iterator;
public interface Cache<K, V> extends Iterable<V>, Serializable { public interface Cache<K, V> extends Iterable<V>, Serializable {
/** /**
* 返回缓存容量,<code>0</code>表示无大小限制 * 返回缓存容量,{@code 0}表示无大小限制
* *
* @return 返回缓存容量,<code>0</code>表示无大小限制 * @return 返回缓存容量,{@code 0}表示无大小限制
*/ */
int capacity(); int capacity();
/** /**
* 缓存失效时长, <code>0</code> 表示没有设置,单位毫秒 * 缓存失效时长, {@code 0} 表示没有设置,单位毫秒
* *
* @return 缓存失效时长, <code>0</code> 表示没有设置,单位毫秒 * @return 缓存失效时长, {@code 0} 表示没有设置,单位毫秒
*/ */
long timeout(); long timeout();
@@ -49,9 +49,9 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
void put(K key, V object, long timeout); void put(K key, V object, long timeout);
/** /**
* 从缓存中获得对象,当对象不在缓存中或已经过期返回<code>null</code> * 从缓存中获得对象,当对象不在缓存中或已经过期返回{@code null}
* <p> * <p>
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回<code>null</code>,否则返回值。 * 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
* <p> * <p>
* 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。 * 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。
* *
@@ -66,7 +66,7 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
/** /**
* 从缓存中获得对象当对象不在缓存中或已经过期返回Func0回调产生的对象 * 从缓存中获得对象当对象不在缓存中或已经过期返回Func0回调产生的对象
* <p> * <p>
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回<code>null</code>,否则返回值。 * 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
* <p> * <p>
* 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。 * 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。
* *
@@ -81,7 +81,7 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
/** /**
* 从缓存中获得对象当对象不在缓存中或已经过期返回Func0回调产生的对象 * 从缓存中获得对象当对象不在缓存中或已经过期返回Func0回调产生的对象
* <p> * <p>
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回<code>null</code>,否则返回值。 * 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
* <p> * <p>
* 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。 * 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。
* *
@@ -93,9 +93,9 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
V get(K key, boolean isUpdateLastAccess, Func0<V> supplier); V get(K key, boolean isUpdateLastAccess, Func0<V> supplier);
/** /**
* 从缓存中获得对象,当对象不在缓存中或已经过期返回<code>null</code> * 从缓存中获得对象,当对象不在缓存中或已经过期返回{@code null}
* <p> * <p>
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回<code>null</code>,否则返回值。 * 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
* *
* @param key 键 * @param key 键
* @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。 * @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。

View File

@@ -135,7 +135,7 @@ public final class UrlBuilder implements Serializable {
* @return UrlBuilder * @return UrlBuilder
*/ */
public static UrlBuilder of(String scheme, String host, int port, String path, String query, String fragment, Charset charset) { public static UrlBuilder of(String scheme, String host, int port, String path, String query, String fragment, Charset charset) {
return of(scheme, host, port, UrlPath.of(path, charset), UrlQuery.of(query, charset), fragment, charset); return of(scheme, host, port, UrlPath.of(path, charset), UrlQuery.of(query, charset, false), fragment, charset);
} }
/** /**
@@ -504,4 +504,4 @@ public final class UrlBuilder implements Serializable {
return build(); return build();
} }
} }

View File

@@ -29,7 +29,7 @@ public class UrlQuery {
* 构建UrlQuery * 构建UrlQuery
* *
* @param queryMap 初始化的查询键值对 * @param queryMap 初始化的查询键值对
* @return {@link UrlQuery} * @return UrlQuery
*/ */
public static UrlQuery of(Map<? extends CharSequence, ?> queryMap) { public static UrlQuery of(Map<? extends CharSequence, ?> queryMap) {
return new UrlQuery(queryMap); return new UrlQuery(queryMap);
@@ -40,11 +40,24 @@ public class UrlQuery {
* *
* @param queryStr 初始化的查询字符串 * @param queryStr 初始化的查询字符串
* @param charset decode用的编码null表示不做decode * @param charset decode用的编码null表示不做decode
* @return {@link UrlQuery} * @return UrlQuery
*/ */
public static UrlQuery of(String queryStr, Charset charset) { public static UrlQuery of(String queryStr, Charset charset) {
return of(queryStr, charset, true);
}
/**
* 构建UrlQuery
*
* @param queryStr 初始化的查询字符串
* @param charset decode用的编码null表示不做decode
* @param autoRemovePath 是否自动去除path部分{@code true}则自动去除第一个?前的内容
* @return UrlQuery
* @since 5.5.8
*/
public static UrlQuery of(String queryStr, Charset charset, boolean autoRemovePath) {
final UrlQuery urlQuery = new UrlQuery(); final UrlQuery urlQuery = new UrlQuery();
urlQuery.parse(queryStr, charset); urlQuery.parse(queryStr, charset, autoRemovePath);
return urlQuery; return urlQuery;
} }
@@ -102,16 +115,31 @@ public class UrlQuery {
* @return this * @return this
*/ */
public UrlQuery parse(String queryStr, Charset charset) { public UrlQuery parse(String queryStr, Charset charset) {
return parse(queryStr, charset, true);
}
/**
* 解析URL中的查询字符串
*
* @param queryStr 查询字符串类似于key1=v1&amp;key2=&amp;key3=v3
* @param charset decode编码null表示不做decode
* @param autoRemovePath 是否自动去除path部分{@code true}则自动去除第一个?前的内容
* @return this
* @since 5.5.8
*/
public UrlQuery parse(String queryStr, Charset charset, boolean autoRemovePath) {
if (StrUtil.isBlank(queryStr)) { if (StrUtil.isBlank(queryStr)) {
return this; return this;
} }
// 去掉Path部分 if (autoRemovePath) {
int pathEndPos = queryStr.indexOf('?'); // 去掉Path部分
if (pathEndPos > -1) { int pathEndPos = queryStr.indexOf('?');
queryStr = StrUtil.subSuf(queryStr, pathEndPos + 1); if (pathEndPos > -1) {
if (StrUtil.isBlank(queryStr)) { queryStr = StrUtil.subSuf(queryStr, pathEndPos + 1);
return this; if (StrUtil.isBlank(queryStr)) {
return this;
}
} }
} }
@@ -135,9 +163,9 @@ public class UrlQuery {
case '&'://键值对之间的分界符 case '&'://键值对之间的分界符
addParam(name, queryStr.substring(pos, i), charset); addParam(name, queryStr.substring(pos, i), charset);
name = null; name = null;
if (i+4 < len && "amp;".equals(queryStr.substring(i + 1, i + 5))) { if (i + 4 < len && "amp;".equals(queryStr.substring(i + 1, i + 5))) {
// issue#850@Github"&amp;"转义为"&" // issue#850@Github"&amp;"转义为"&"
i+=4; i += 4;
} }
// 开始位置从分节符后开始 // 开始位置从分节符后开始
pos = i + 1; pos = i + 1;

View File

@@ -332,7 +332,7 @@ public class URLUtil {
* @throws UtilException UnsupportedEncodingException * @throws UtilException UnsupportedEncodingException
*/ */
public static String encodeAll(String url, Charset charset) throws UtilException { public static String encodeAll(String url, Charset charset) throws UtilException {
if (null == charset) { if (null == charset || StrUtil.isEmpty(url)) {
return url; return url;
} }
@@ -870,4 +870,4 @@ public class URLUtil {
return builder.toString(); return builder.toString();
} }
} }

View File

@@ -6,6 +6,10 @@ import cn.hutool.core.util.CharsetUtil;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
public class UrlBuilderTest { public class UrlBuilderTest {
@Test @Test
@@ -217,4 +221,18 @@ public class UrlBuilderTest {
final UrlBuilder urlBuilder = UrlBuilder.ofHttp("https://hutool.cn//file/test.jpg", CharsetUtil.CHARSET_UTF_8); final UrlBuilder urlBuilder = UrlBuilder.ofHttp("https://hutool.cn//file/test.jpg", CharsetUtil.CHARSET_UTF_8);
Assert.assertEquals("https://hutool.cn//file/test.jpg", urlBuilder.toString()); Assert.assertEquals("https://hutool.cn//file/test.jpg", urlBuilder.toString());
} }
@Test
public void toURITest() throws URISyntaxException {
String webUrl = "http://exmple.com/patha/pathb?a=123"; // 报错数据
final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8);
Assert.assertEquals(new URI(webUrl), urlBuilder.toURI());
}
@Test
public void testEncodeInQuery() {
String webUrl = "http://exmple.com/patha/pathb?a=123&b=4?6&c=789"; // b=4?6 参数中有未编码的?
final UrlBuilder urlBuilder = UrlBuilder.of(webUrl, StandardCharsets.UTF_8);
Assert.assertEquals("a=123&b=4%3F6&c=789", urlBuilder.getQueryStr());
}
} }