mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
fix plus encode bug
This commit is contained in:
parent
6b0b729c68
commit
1edbc0de80
@ -22,6 +22,7 @@
|
|||||||
* 【core 】 修复Validator.isUrl()传空返回true(issue#I3ETTY@Gitee)
|
* 【core 】 修复Validator.isUrl()传空返回true(issue#I3ETTY@Gitee)
|
||||||
* 【db 】 修复数据库driver根据url的判断识别错误问题(issue#I3EWBI@Gitee)
|
* 【db 】 修复数据库driver根据url的判断识别错误问题(issue#I3EWBI@Gitee)
|
||||||
* 【json 】 修复JSONStrFormatter换行多余空行问题(issue#I3FA8B@Gitee)
|
* 【json 】 修复JSONStrFormatter换行多余空行问题(issue#I3FA8B@Gitee)
|
||||||
|
* 【core 】 修复UrlPath中的+被转义为空格%20的问题(issue#1501@Github)
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -23,24 +23,84 @@ public class URLDecoder implements Serializable {
|
|||||||
|
|
||||||
private static final byte ESCAPE_CHAR = '%';
|
private static final byte ESCAPE_CHAR = '%';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码,不对+解码
|
||||||
|
* <pre>
|
||||||
|
* 1. 将%20转换为空格 ;
|
||||||
|
* 2. 将"%xy"转换为文本形式,xy是两位16进制的数值;
|
||||||
|
* 3. 跳过不符合规范的%形式,直接输出
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str 包含URL编码后的字符串
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 解码后的字符串
|
||||||
|
*/
|
||||||
|
public static String decodeForPath(String str, Charset charset) {
|
||||||
|
return decode(str, charset, false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解码
|
* 解码
|
||||||
|
* <pre>
|
||||||
|
* 1. 将+和%20转换为空格 ;
|
||||||
|
* 2. 将"%xy"转换为文本形式,xy是两位16进制的数值;
|
||||||
|
* 3. 跳过不符合规范的%形式,直接输出
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param str 包含URL编码后的字符串
|
* @param str 包含URL编码后的字符串
|
||||||
* @param charset 编码
|
* @param charset 编码
|
||||||
* @return 解码后的字符串
|
* @return 解码后的字符串
|
||||||
*/
|
*/
|
||||||
public static String decode(String str, Charset charset) {
|
public static String decode(String str, Charset charset) {
|
||||||
return StrUtil.str(decode(StrUtil.bytes(str, charset)), charset);
|
return decode(str, charset, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解码
|
* 解码
|
||||||
|
* <pre>
|
||||||
|
* 1. 将%20转换为空格 ;
|
||||||
|
* 2. 将"%xy"转换为文本形式,xy是两位16进制的数值;
|
||||||
|
* 3. 跳过不符合规范的%形式,直接输出
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param str 包含URL编码后的字符串
|
||||||
|
* @param isPlusToSpace 是否+转换为空格
|
||||||
|
* @param charset 编码
|
||||||
|
* @return 解码后的字符串
|
||||||
|
*/
|
||||||
|
public static String decode(String str, Charset charset, boolean isPlusToSpace) {
|
||||||
|
return StrUtil.str(decode(StrUtil.bytes(str, charset), isPlusToSpace), charset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码
|
||||||
|
* <pre>
|
||||||
|
* 1. 将+和%20转换为空格 ;
|
||||||
|
* 2. 将"%xy"转换为文本形式,xy是两位16进制的数值;
|
||||||
|
* 3. 跳过不符合规范的%形式,直接输出
|
||||||
|
* </pre>
|
||||||
*
|
*
|
||||||
* @param bytes url编码的bytes
|
* @param bytes url编码的bytes
|
||||||
* @return 解码后的bytes
|
* @return 解码后的bytes
|
||||||
*/
|
*/
|
||||||
public static byte[] decode(byte[] bytes) {
|
public static byte[] decode(byte[] bytes) {
|
||||||
|
return decode(bytes, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码
|
||||||
|
* <pre>
|
||||||
|
* 1. 将%20转换为空格 ;
|
||||||
|
* 2. 将"%xy"转换为文本形式,xy是两位16进制的数值;
|
||||||
|
* 3. 跳过不符合规范的%形式,直接输出
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param bytes url编码的bytes
|
||||||
|
* @param isPlusToSpace 是否+转换为空格
|
||||||
|
* @return 解码后的bytes
|
||||||
|
* @since 5.6.3
|
||||||
|
*/
|
||||||
|
public static byte[] decode(byte[] bytes, boolean isPlusToSpace) {
|
||||||
if (bytes == null) {
|
if (bytes == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -49,7 +109,7 @@ public class URLDecoder implements Serializable {
|
|||||||
for (int i = 0; i < bytes.length; i++) {
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
b = bytes[i];
|
b = bytes[i];
|
||||||
if (b == '+') {
|
if (b == '+') {
|
||||||
buffer.write(CharUtil.SPACE);
|
buffer.write(isPlusToSpace ? CharUtil.SPACE : b);
|
||||||
} else if (b == ESCAPE_CHAR) {
|
} else if (b == ESCAPE_CHAR) {
|
||||||
if (i + 1 < bytes.length) {
|
if (i + 1 < bytes.length) {
|
||||||
final int u = CharUtil.digit16(bytes[i + 1]);
|
final int u = CharUtil.digit16(bytes[i + 1]);
|
||||||
|
@ -82,6 +82,17 @@ public final class UrlBuilder implements Serializable {
|
|||||||
return ofHttp(httpUrl, null);
|
return ofHttp(httpUrl, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用URL字符串构建UrlBuilder,当传入的URL没有协议时,按照http协议对待,编码默认使用UTF-8
|
||||||
|
*
|
||||||
|
* @param httpUrl URL字符串
|
||||||
|
* @return UrlBuilder
|
||||||
|
* @since 5.6.3
|
||||||
|
*/
|
||||||
|
public static UrlBuilder ofHttp(String httpUrl) {
|
||||||
|
return ofHttp(httpUrl, CharsetUtil.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用URL字符串构建UrlBuilder,当传入的URL没有协议时,按照http协议对待。
|
* 使用URL字符串构建UrlBuilder,当传入的URL没有协议时,按照http协议对待。
|
||||||
*
|
*
|
||||||
@ -99,6 +110,16 @@ public final class UrlBuilder implements Serializable {
|
|||||||
return of(httpUrl, charset);
|
return of(httpUrl, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用URL字符串构建UrlBuilder,默认使用UTF-8编码
|
||||||
|
*
|
||||||
|
* @param url URL字符串
|
||||||
|
* @return UrlBuilder
|
||||||
|
*/
|
||||||
|
public static UrlBuilder of(String url) {
|
||||||
|
return of(url, CharsetUtil.CHARSET_UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 使用URL字符串构建UrlBuilder
|
* 使用URL字符串构建UrlBuilder
|
||||||
*
|
*
|
||||||
|
@ -2,6 +2,7 @@ package cn.hutool.core.net.url;
|
|||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.net.URLDecoder;
|
||||||
import cn.hutool.core.util.CharUtil;
|
import cn.hutool.core.util.CharUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.core.util.URLUtil;
|
import cn.hutool.core.util.URLUtil;
|
||||||
@ -97,8 +98,6 @@ public class UrlPath {
|
|||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public UrlPath parse(String path, Charset charset) {
|
public UrlPath parse(String path, Charset charset) {
|
||||||
UrlPath urlPath = new UrlPath();
|
|
||||||
|
|
||||||
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)){
|
||||||
@ -108,11 +107,11 @@ public class UrlPath {
|
|||||||
path = fixPath(path);
|
path = fixPath(path);
|
||||||
final List<String> split = StrUtil.split(path, '/');
|
final List<String> split = StrUtil.split(path, '/');
|
||||||
for (String seg : split) {
|
for (String seg : split) {
|
||||||
addInternal(URLUtil.decode(seg, charset), false);
|
addInternal(URLDecoder.decodeForPath(seg, charset), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return urlPath;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +134,7 @@ public class URLUtil {
|
|||||||
* @return URL
|
* @return URL
|
||||||
* @since 5.5.2
|
* @since 5.5.2
|
||||||
*/
|
*/
|
||||||
public static URI getStringURI(CharSequence content){
|
public static URI getStringURI(CharSequence content) {
|
||||||
final String contentStr = StrUtil.addPrefixIfNot(content, "string:///");
|
final String contentStr = StrUtil.addPrefixIfNot(content, "string:///");
|
||||||
return URI.create(contentStr);
|
return URI.create(contentStr);
|
||||||
}
|
}
|
||||||
@ -467,6 +467,23 @@ public class URLUtil {
|
|||||||
return URLDecoder.decode(content, charset);
|
return URLDecoder.decode(content, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解码application/x-www-form-urlencoded字符<br>
|
||||||
|
* 将%开头的16进制表示的内容解码。
|
||||||
|
*
|
||||||
|
* @param content 被解码内容
|
||||||
|
* @param charset 编码,null表示不解码
|
||||||
|
* @param isPlusToSpace 是否+转换为空格
|
||||||
|
* @return 编码后的字符
|
||||||
|
* @since 5.6.3
|
||||||
|
*/
|
||||||
|
public static String decode(String content, Charset charset, boolean isPlusToSpace) {
|
||||||
|
if (null == charset) {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return URLDecoder.decode(content, charset, isPlusToSpace);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 解码application/x-www-form-urlencoded字符<br>
|
* 解码application/x-www-form-urlencoded字符<br>
|
||||||
* 将%开头的16进制表示的内容解码。
|
* 将%开头的16进制表示的内容解码。
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package cn.hutool.core.net;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class URLEncoderTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encodeTest(){
|
||||||
|
String encode = URLEncoder.DEFAULT.encode("+", CharsetUtil.CHARSET_UTF_8);
|
||||||
|
Assert.assertEquals("+", encode);
|
||||||
|
|
||||||
|
encode = URLEncoder.DEFAULT.encode(" ", CharsetUtil.CHARSET_UTF_8);
|
||||||
|
Assert.assertEquals("%20", encode);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,13 @@ public class UrlBuilderTest {
|
|||||||
Assert.assertEquals("http://www.hutool.cn/", buildUrl);
|
Assert.assertEquals("http://www.hutool.cn/", buildUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void buildTest2() {
|
||||||
|
// path中的+不做处理
|
||||||
|
String buildUrl = UrlBuilder.ofHttp("http://www.hutool.cn/+8618888888888", CharsetUtil.CHARSET_UTF_8).build();
|
||||||
|
Assert.assertEquals("http://www.hutool.cn/+8618888888888", buildUrl);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHost() {
|
public void testHost() {
|
||||||
String buildUrl = UrlBuilder.create()
|
String buildUrl = UrlBuilder.create()
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package cn.hutool.core.net;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.CharsetUtil;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class UrlDecoderTest {
|
||||||
|
@Test
|
||||||
|
public void decodeForPathTest(){
|
||||||
|
Assert.assertEquals("+", URLDecoder.decodeForPath("+", CharsetUtil.CHARSET_UTF_8));
|
||||||
|
}
|
||||||
|
}
|
@ -77,7 +77,7 @@ public enum ContentType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 是否为默认Content-Type,默认包括<code>null</code>和application/x-www-form-urlencoded
|
* 是否为默认Content-Type,默认包括{@code null}和application/x-www-form-urlencoded
|
||||||
*
|
*
|
||||||
* @param contentType 内容类型
|
* @param contentType 内容类型
|
||||||
* @return 是否为默认Content-Type
|
* @return 是否为默认Content-Type
|
||||||
|
@ -12,7 +12,6 @@ import cn.hutool.core.lang.Assert;
|
|||||||
import cn.hutool.core.map.MapUtil;
|
import cn.hutool.core.map.MapUtil;
|
||||||
import cn.hutool.core.net.url.UrlBuilder;
|
import cn.hutool.core.net.url.UrlBuilder;
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
import cn.hutool.core.util.CharsetUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.http.body.MultipartBody;
|
import cn.hutool.http.body.MultipartBody;
|
||||||
@ -157,7 +156,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
|||||||
* @param url URL
|
* @param url URL
|
||||||
*/
|
*/
|
||||||
public HttpRequest(String url) {
|
public HttpRequest(String url) {
|
||||||
this(UrlBuilder.ofHttp(url, CharsetUtil.CHARSET_UTF_8));
|
this(UrlBuilder.ofHttp(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user