This commit is contained in:
Looly 2022-07-20 13:38:55 +08:00
parent 28fde6ddcf
commit 32235d3b7e
4 changed files with 55 additions and 17 deletions

View File

@ -85,8 +85,7 @@ public class Console {
public static void log(final Throwable t, final String template, final Object... values) { public static void log(final Throwable t, final String template, final Object... values) {
out.println(StrUtil.format(template, values)); out.println(StrUtil.format(template, values));
if (null != t) { if (null != t) {
//noinspection CallToPrintStackTrace t.printStackTrace(out);
t.printStackTrace();
out.flush(); out.flush();
} }
} }

View File

@ -340,6 +340,22 @@ public final class UrlBuilder implements Builder<String> {
return this; return this;
} }
/**
* 是否path的末尾加 /
*
* @param withEngTag 是否path的末尾加 /
* @return this
* @since 5.8.5
*/
public UrlBuilder setWithEndTag(final boolean withEngTag) {
if (null == this.path) {
this.path = UrlPath.of();
}
this.path.setWithEndTag(withEngTag);
return this;
}
/** /**
* 增加路径节点路径节点中的"/"会被转义为"%2F" * 增加路径节点路径节点中的"/"会被转义为"%2F"
* *
@ -489,7 +505,7 @@ public final class UrlBuilder implements Builder<String> {
final StringBuilder fileBuilder = new StringBuilder(); final StringBuilder fileBuilder = new StringBuilder();
// path // path
fileBuilder.append(StrUtil.blankToDefault(getPathStr(), StrUtil.SLASH)); fileBuilder.append(getPathStr());
// query // query
final String query = getQueryStr(); final String query = getQueryStr();

View File

@ -22,6 +22,16 @@ public class UrlPath {
private List<String> segments; private List<String> segments;
private boolean withEngTag; private boolean withEngTag;
/**
* 构建UrlPath
*
* @return UrlPath
* @since 6.0.0
*/
public static UrlPath of() {
return new UrlPath();
}
/** /**
* 构建UrlPath * 构建UrlPath
* *
@ -30,9 +40,7 @@ public class UrlPath {
* @return UrlPath * @return UrlPath
*/ */
public static UrlPath of(final CharSequence pathStr, final Charset charset) { public static UrlPath of(final CharSequence pathStr, final Charset charset) {
final UrlPath urlPath = new UrlPath(); return of().parse(pathStr, charset);
urlPath.parse(pathStr, charset);
return urlPath;
} }
/** /**
@ -100,12 +108,12 @@ public class UrlPath {
public UrlPath parse(CharSequence path, final Charset charset) { public UrlPath parse(CharSequence path, final Charset charset) {
if (StrUtil.isNotEmpty(path)) { if (StrUtil.isNotEmpty(path)) {
// 原URL中以/结尾则这个规则需保留issue#I1G44J@Gitee // 原URL中以/结尾则这个规则需保留issue#I1G44J@Gitee
if(StrUtil.endWith(path, CharUtil.SLASH)){ if (StrUtil.endWith(path, CharUtil.SLASH)) {
this.withEngTag = true; this.withEngTag = true;
} }
path = fixPath(path); path = fixPath(path);
if(StrUtil.isNotEmpty(path)){ if (StrUtil.isNotEmpty(path)) {
final List<String> split = StrUtil.split(path, '/'); final List<String> split = StrUtil.split(path, '/');
for (final String seg : split) { for (final String seg : split) {
addInternal(URLDecoder.decodeForPath(seg, charset), false); addInternal(URLDecoder.decodeForPath(seg, charset), false);
@ -142,13 +150,14 @@ public class UrlPath {
*/ */
public String build(final Charset charset, final boolean encodePercent) { public String build(final Charset charset, final boolean encodePercent) {
if (CollUtil.isEmpty(this.segments)) { if (CollUtil.isEmpty(this.segments)) {
return StrUtil.EMPTY; // 没有节点的path取决于是否末尾追加/如果不追加返回空串否则返回/
return withEngTag ? StrUtil.SLASH : StrUtil.EMPTY;
} }
final char[] safeChars = encodePercent ? null : new char[]{'%'}; final char[] safeChars = encodePercent ? null : new char[]{'%'};
final StringBuilder builder = new StringBuilder(); final StringBuilder builder = new StringBuilder();
for (final String segment : segments) { for (final String segment : segments) {
if(builder.length() == 0){ if (builder.length() == 0) {
// 根据https://www.ietf.org/rfc/rfc3986.html#section-3.3定义 // 根据https://www.ietf.org/rfc/rfc3986.html#section-3.3定义
// path的第一部分不允许有":"其余部分允许 // path的第一部分不允许有":"其余部分允许
// 在此处的Path部分特指host之后的部分即不包含第一部分 // 在此处的Path部分特指host之后的部分即不包含第一部分
@ -157,13 +166,16 @@ public class UrlPath {
builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset, safeChars)); builder.append(CharUtil.SLASH).append(RFC3986.SEGMENT.encode(segment, charset, safeChars));
} }
} }
if (withEngTag) {
if (StrUtil.isEmpty(builder)) { if (StrUtil.isEmpty(builder)) {
// 空白追加是保证以/开头 // 空白追加是保证以/开头
builder.append(CharUtil.SLASH); builder.append(CharUtil.SLASH);
}else if (withEngTag && false == StrUtil.endWith(builder, CharUtil.SLASH)) { } else if (false == StrUtil.endWith(builder, CharUtil.SLASH)) {
// 尾部没有/则追加否则不追加 // 尾部没有/则追加否则不追加
builder.append(CharUtil.SLASH); builder.append(CharUtil.SLASH);
} }
}
return builder.toString(); return builder.toString();
} }

View File

@ -20,6 +20,17 @@ public class UrlBuilderTest {
Assert.assertEquals("http://www.hutool.cn/", buildUrl); Assert.assertEquals("http://www.hutool.cn/", buildUrl);
} }
@Test
public void buildWithoutSlashTest(){
// https://github.com/dromara/hutool/issues/2459
String buildUrl = UrlBuilder.of().setScheme("http").setHost("192.168.1.1").setPort(8080).setWithEndTag(false).build();
Assert.assertEquals("http://192.168.1.1:8080", buildUrl);
buildUrl = UrlBuilder.of().setScheme("http").setHost("192.168.1.1").setPort(8080).addQuery("url", "http://192.168.1.1/test/1")
.setWithEndTag(false).build();
Assert.assertEquals("http://192.168.1.1:8080?url=http://192.168.1.1/test/1", buildUrl);
}
@Test @Test
public void buildTest2() { public void buildTest2() {
// path中的+不做处理 // path中的+不做处理