mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
fix code
This commit is contained in:
@@ -39,7 +39,7 @@ import java.nio.charset.Charset;
|
||||
*/
|
||||
public final class UrlBuilder implements Builder<String> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String DEFAULT_SCHEME = "http";
|
||||
private static final String DEFAULT_SCHEME = "http" ;
|
||||
|
||||
/**
|
||||
* 协议,例如http
|
||||
@@ -70,11 +70,6 @@ public final class UrlBuilder implements Builder<String> {
|
||||
* 编码,用于URLEncode和URLDecode
|
||||
*/
|
||||
private Charset charset;
|
||||
/**
|
||||
* 是否需要编码`%`<br>
|
||||
* 区别对待,如果是,则生成URL时需要重新全部编码,否则跳过所有`%`
|
||||
*/
|
||||
private final boolean needEncodePercent;
|
||||
|
||||
// region ----- of
|
||||
|
||||
@@ -124,10 +119,15 @@ public final class UrlBuilder implements Builder<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用URL字符串构建UrlBuilder,当传入的URL没有协议时,按照http协议对待。
|
||||
* 使用URL字符串构建UrlBuilder,当传入的URL没有协议时,按照http协议对待。<br>
|
||||
* <ul>
|
||||
* <li>如果url用户传入的URL没有做编码,则charset设置为{@code null},此时URL不会解码,在build时也不会编码。</li>
|
||||
* <li>如果url已经编码,或部分编码,则需要设置charset,此时URL会解码编码后的参数,在build时也会编码。</li>
|
||||
* <li>如果url未编码,且存在歧义字符串,则需要设置charset为{@code null},并调用{@link #setCharset(Charset)}在build时编码URL。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param httpUrl URL字符串
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @param charset 编码,用于URLEncode和URLDecode,如果为{@code null},则不对传入的URL解码
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder ofHttp(String httpUrl, final Charset charset) {
|
||||
@@ -154,7 +154,12 @@ public final class UrlBuilder implements Builder<String> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用URL字符串构建UrlBuilder
|
||||
* 使用URL字符串构建UrlBuilder,规则如下:<br>
|
||||
* <ul>
|
||||
* <li>如果url用户传入的URL没有做编码,则charset设置为{@code null},此时URL不会解码,在build时也不会编码。</li>
|
||||
* <li>如果url已经编码,或部分编码,则需要设置charset,此时URL会解码编码后的参数,在build时也会编码。</li>
|
||||
* <li>如果url未编码,且存在歧义字符串,则需要设置charset为{@code null},并调用{@link #setCharset(Charset)}在build时编码URL。</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param url URL字符串
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
@@ -169,7 +174,7 @@ public final class UrlBuilder implements Builder<String> {
|
||||
* 使用URL构建UrlBuilder
|
||||
*
|
||||
* @param url URL
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @param charset 编码,用于URLEncode和URLDecode,{@code null}表示不解码
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder of(final URL url, final Charset charset) {
|
||||
@@ -225,8 +230,6 @@ public final class UrlBuilder implements Builder<String> {
|
||||
*/
|
||||
public UrlBuilder() {
|
||||
this.charset = CharsetUtil.UTF_8;
|
||||
// 编码非空情况下做解码
|
||||
this.needEncodePercent = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,8 +251,6 @@ public final class UrlBuilder implements Builder<String> {
|
||||
this.path = path;
|
||||
this.query = query;
|
||||
this.setFragment(fragment);
|
||||
// 编码非空情况下做解码
|
||||
this.needEncodePercent = null != charset;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -360,7 +361,7 @@ public final class UrlBuilder implements Builder<String> {
|
||||
* @return 路径,例如/aa/bb/cc
|
||||
*/
|
||||
public String getPathStr() {
|
||||
return null == this.path ? StrUtil.SLASH : this.path.build(charset, this.needEncodePercent);
|
||||
return null == this.path ? StrUtil.SLASH : this.path.build(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -435,7 +436,7 @@ public final class UrlBuilder implements Builder<String> {
|
||||
* @return 查询语句,例如a=1&b=2
|
||||
*/
|
||||
public String getQueryStr() {
|
||||
return null == this.query ? null : this.query.build(this.charset, this.needEncodePercent);
|
||||
return null == this.query ? null : this.query.build(this.charset);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -483,8 +484,7 @@ public final class UrlBuilder implements Builder<String> {
|
||||
* @return 标识符,例如#后边的部分
|
||||
*/
|
||||
public String getFragmentEncoded() {
|
||||
final char[] safeChars = this.needEncodePercent ? null : new char[]{'%'};
|
||||
return RFC3986.FRAGMENT.encode(this.fragment, this.charset, safeChars);
|
||||
return RFC3986.FRAGMENT.encode(this.fragment, this.charset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -47,7 +47,7 @@ public class UrlDecoder implements Serializable {
|
||||
* </ol>
|
||||
*
|
||||
* @param str 包含URL编码后的字符串
|
||||
* @param charset 编码
|
||||
* @param charset 解码的编码,{@code null}表示不做解码
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String decodeForPath(final String str, final Charset charset) {
|
||||
@@ -113,7 +113,7 @@ public class UrlDecoder implements Serializable {
|
||||
*
|
||||
* @param str 包含URL编码后的字符串
|
||||
* @param isPlusToSpace 是否+转换为空格
|
||||
* @param charset 编码,{@code null}表示不做编码
|
||||
* @param charset 编码,{@code null}表示不做解码
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String decode(final String str, final Charset charset, final boolean isPlusToSpace) {
|
||||
|
@@ -137,19 +137,6 @@ public class UrlPath {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建path,前面带'/'<br>
|
||||
* <pre>
|
||||
* path = path-abempty / path-absolute / path-noscheme / path-rootless / path-empty
|
||||
* </pre>
|
||||
*
|
||||
* @param charset encode编码,null表示不做encode
|
||||
* @return 如果没有任何内容,则返回空字符串""
|
||||
*/
|
||||
public String build(final Charset charset) {
|
||||
return build(charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建path,前面带'/'<br>
|
||||
* <pre>
|
||||
@@ -157,22 +144,20 @@ public class UrlPath {
|
||||
* </pre>
|
||||
*
|
||||
* @param charset encode编码,null表示不做encode
|
||||
* @param encodePercent 是否编码`%`
|
||||
* @return 如果没有任何内容,则返回空字符串""
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public String build(final Charset charset, final boolean encodePercent) {
|
||||
public String build(final Charset charset) {
|
||||
if (CollUtil.isEmpty(this.segments)) {
|
||||
// 没有节点的path取决于是否末尾追加/,如果不追加返回空串,否则返回/
|
||||
return withEngTag ? StrUtil.SLASH : StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
final char[] safeChars = encodePercent ? null : new char[]{'%'};
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (final String segment : segments) {
|
||||
// https://www.ietf.org/rfc/rfc3986.html#section-3.3
|
||||
// 此处Path中是允许有`:`的,之前理解有误,应该是相对URI的第一个segment中不允许有`:`
|
||||
builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset, safeChars));
|
||||
builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset));
|
||||
}
|
||||
|
||||
if (withEngTag) {
|
||||
|
@@ -247,54 +247,20 @@ public class UrlQuery {
|
||||
* <li>如果value为{@code null},只保留key,如key1对应value为{@code null}生成类似于{@code key1&key2=v2}形式</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param charset encode编码,null表示不做encode编码
|
||||
* @param charset encode编码,null表示不做encode编码
|
||||
* @return URL查询字符串
|
||||
*/
|
||||
public String build(final Charset charset) {
|
||||
return build(charset, null != charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL查询字符串,即将key-value键值对转换为{@code key1=v1&key2=v2&key3=v3}形式。<br>
|
||||
* 对于{@code null}处理规则如下:
|
||||
* <ul>
|
||||
* <li>如果key为{@code null},则这个键值对忽略</li>
|
||||
* <li>如果value为{@code null},只保留key,如key1对应value为{@code null}生成类似于{@code key1&key2=v2}形式</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param charset encode编码,null表示不做encode编码
|
||||
* @param encodePercent 是否编码`%`
|
||||
* @return URL查询字符串
|
||||
*/
|
||||
public String build(final Charset charset, final boolean encodePercent) {
|
||||
switch (this.encodeMode) {
|
||||
case FORM_URL_ENCODED:
|
||||
return build(FormUrlencoded.ALL, FormUrlencoded.ALL, charset, encodePercent);
|
||||
return build(FormUrlencoded.ALL, FormUrlencoded.ALL, charset);
|
||||
case STRICT:
|
||||
return build(RFC3986.QUERY_PARAM_NAME_STRICT, RFC3986.QUERY_PARAM_VALUE_STRICT, charset, encodePercent);
|
||||
return build(RFC3986.QUERY_PARAM_NAME_STRICT, RFC3986.QUERY_PARAM_VALUE_STRICT, charset);
|
||||
default:
|
||||
return build(RFC3986.QUERY_PARAM_NAME, RFC3986.QUERY_PARAM_VALUE, charset, encodePercent);
|
||||
return build(RFC3986.QUERY_PARAM_NAME, RFC3986.QUERY_PARAM_VALUE, charset);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL查询字符串,即将key-value键值对转换为{@code key1=v1&key2=v2&key3=v3}形式。<br>
|
||||
* 对于{@code null}处理规则如下:
|
||||
* <ul>
|
||||
* <li>如果key为{@code null},则这个键值对忽略</li>
|
||||
* <li>如果value为{@code null},只保留key,如key1对应value为{@code null}生成类似于{@code key1&key2=v2}形式</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param keyCoder 键值对中键的编码器
|
||||
* @param valueCoder 键值对中值的编码器
|
||||
* @param charset encode编码,null表示不做encode编码
|
||||
* @return URL查询字符串
|
||||
* @since 5.7.16
|
||||
*/
|
||||
public String build(final PercentCodec keyCoder, final PercentCodec valueCoder, final Charset charset) {
|
||||
return build(keyCoder, valueCoder, charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL查询字符串,即将key-value键值对转换为{@code key1=v1&key2=v2&key3=v3}形式。<br>
|
||||
* 对于{@code null}处理规则如下:
|
||||
@@ -306,17 +272,15 @@ public class UrlQuery {
|
||||
* @param keyCoder 键值对中键的编码器
|
||||
* @param valueCoder 键值对中值的编码器
|
||||
* @param charset encode编码,null表示不做encode编码
|
||||
* @param encodePercent 是否编码`%`
|
||||
* @return URL查询字符串
|
||||
* @since 5.8.0
|
||||
*/
|
||||
public String build(final PercentCodec keyCoder, final PercentCodec valueCoder,
|
||||
final Charset charset, final boolean encodePercent) {
|
||||
final Charset charset) {
|
||||
if (MapUtil.isEmpty(this.query)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
final char[] safeChars = encodePercent ? null : new char[]{'%'};
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
CharSequence name;
|
||||
CharSequence value;
|
||||
@@ -326,10 +290,10 @@ public class UrlQuery {
|
||||
if (sb.length() > 0) {
|
||||
sb.append("&");
|
||||
}
|
||||
sb.append(keyCoder.encode(name, charset, safeChars));
|
||||
sb.append(keyCoder.encode(name, charset));
|
||||
value = entry.getValue();
|
||||
if (null != value) {
|
||||
sb.append("=").append(valueCoder.encode(value, charset, safeChars));
|
||||
sb.append("=").append(valueCoder.encode(value, charset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -449,7 +449,7 @@ public class UrlBuilderTest {
|
||||
// https://github.com/dromara/hutool/issues/2243
|
||||
// 如果用户已经做了%编码,不应该重复编码
|
||||
final String url = "https://hutool.cn/v1.0?privateNum=%2B8616512884988";
|
||||
final String s = UrlBuilder.of(url, null).setCharset(CharsetUtil.UTF_8).toString();
|
||||
final String s = UrlBuilder.of(url, null).toString();
|
||||
assertEquals(url, s);
|
||||
}
|
||||
|
||||
@@ -528,5 +528,26 @@ public class UrlBuilderTest {
|
||||
// 不指定编码,则保持原字符串
|
||||
urlBuilder = UrlBuilder.ofHttp("http://localhost:9999/getReportDataList?goodsName=工业硫酸98%&conReportTypeId=1", null);
|
||||
assertEquals("http://localhost:9999/getReportDataList?goodsName=工业硫酸98%&conReportTypeId=1", urlBuilder.build());
|
||||
|
||||
// URL已经编码,则先解码,再重新编码
|
||||
// 此例子中98%的%未做编码,解析后编码为25%
|
||||
urlBuilder = UrlBuilder.ofHttp("http://localhost:9999/getReportDataList?goodsName=%E5%B7%A5%E4%B8%9A%E7%A1%AB%E9%85%B898%&conReportTypeId=1", CharsetUtil.UTF_8);
|
||||
assertEquals("http://localhost:9999/getReportDataList?goodsName=%E5%B7%A5%E4%B8%9A%E7%A1%AB%E9%85%B898%25&conReportTypeId=1", urlBuilder.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
void percentTest() {
|
||||
// 此处URL有歧义
|
||||
// 如果用户需要传的a的值为`%`,则这个URL表示已经编码过了,此时需要解码后再重新编码,保持不变
|
||||
UrlBuilder urlBuilder = UrlBuilder.ofHttp("http://localhost:9999/a?a=%25");
|
||||
assertEquals("http://localhost:9999/a?a=%25", urlBuilder.build());
|
||||
// 不传charset,则保留原样,不做任何处理
|
||||
urlBuilder = UrlBuilder.ofHttp("http://localhost:9999/a?a=%25", null);
|
||||
assertEquals("http://localhost:9999/a?a=%25", urlBuilder.build());
|
||||
|
||||
// 如果用户需要传的a的值为`%25`,则这个URL表示未编码,不需要解码,需要对`%`再次编码
|
||||
urlBuilder = UrlBuilder.ofHttp("http://localhost:9999/a?a=%25", null);
|
||||
urlBuilder.setCharset(CharsetUtil.UTF_8);
|
||||
assertEquals("http://localhost:9999/a?a=%2525", urlBuilder.build());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user