mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
add UrlDecoder
This commit is contained in:
parent
8102b31373
commit
ca7c407a1c
@ -8,11 +8,15 @@
|
||||
### 新特性
|
||||
* 【core 】 ListUtil、MapUtil、CollUtil增加empty方法
|
||||
* 【poi 】 调整别名策略,clearHeaderAlias和addHeaderAlias同时清除aliasComparator(issue#828@Github)
|
||||
* 【core 】 修改StrUtil.equals逻辑,改为contentEquals
|
||||
* 【core 】 增加URLUtil.UrlDecoder
|
||||
|
||||
### Bug修复
|
||||
* 【json 】 修复解析JSON字符串时配置无法传递问题
|
||||
* 【core 】 修复ServletUtil.readCookieMap空指针问题(issue#827@Github)
|
||||
* 【crypto 】 修复SM2中检查密钥导致的问题(issue#I1EC47@Gitee)
|
||||
* 【core 】 修复TableMap.isEmpty判断问题
|
||||
* 【http 】 修复编码后的URL传入导致二次编码的问题(issue#I1EIMN@Gitee)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
@ -2,7 +2,6 @@ package cn.hutool.core.map;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
@ -10,6 +9,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -24,7 +25,7 @@ import java.util.Set;
|
||||
* @param <V> 值类型
|
||||
* @author looly
|
||||
*/
|
||||
public class TableMap<K, V> implements Map<K, V>, Serializable {
|
||||
public class TableMap<K, V> implements Map<K, V>, Iterable<Map.Entry<K, V>>, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<K> keys;
|
||||
@ -58,7 +59,7 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return ArrayUtil.isEmpty(keys);
|
||||
return CollUtil.isEmpty(keys);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -159,13 +160,37 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Set<Map.Entry<K, V>> entrySet() {
|
||||
HashSet<Map.Entry<K, V>> hashSet = new HashSet<>();
|
||||
final Set<Map.Entry<K, V>> hashSet = new LinkedHashSet<>();
|
||||
for (int i = 0; i < size(); i++) {
|
||||
hashSet.add(new Entry<>(keys.get(i), values.get(i)));
|
||||
}
|
||||
return hashSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<K, V>> iterator() {
|
||||
return new Iterator<Map.Entry<K, V>>() {
|
||||
private final Iterator<K> keysIter = keys.iterator();
|
||||
private final Iterator<V> valuesIter = values.iterator();
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return keysIter.hasNext() && valuesIter.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<K, V> next() {
|
||||
return new Entry<>(keysIter.next(), valuesIter.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
keysIter.remove();
|
||||
valuesIter.remove();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class Entry<K, V> implements Map.Entry<K, V> {
|
||||
|
||||
private final K key;
|
||||
|
73
hutool-core/src/main/java/cn/hutool/core/net/URLDecoder.java
Normal file
73
hutool-core/src/main/java/cn/hutool/core/net/URLDecoder.java
Normal file
@ -0,0 +1,73 @@
|
||||
package cn.hutool.core.net;
|
||||
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* URL解码,数据内容的类型是 application/x-www-form-urlencoded。
|
||||
*
|
||||
* <pre>
|
||||
* 1. 将%20转换为空格 ;
|
||||
* 2. 将"%xy"转换为文本形式,xy是两位16进制的数值;
|
||||
* 3. 跳过不符合规范的%形式,直接输出
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public class URLDecoder implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final byte ESCAPE_CHAR = '%';
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param str 包含URL编码后的字符串
|
||||
* @param charset 编码
|
||||
* @return 解码后的字符串
|
||||
*/
|
||||
public static String decode(String str, Charset charset) {
|
||||
return StrUtil.str(decode(StrUtil.bytes(str, charset)), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码
|
||||
*
|
||||
* @param bytes url编码的bytes
|
||||
* @return 解码后的bytes
|
||||
*/
|
||||
public static byte[] decode(byte[] bytes) {
|
||||
if (bytes == null) {
|
||||
return null;
|
||||
}
|
||||
final ByteArrayOutputStream buffer = new ByteArrayOutputStream(bytes.length);
|
||||
int b;
|
||||
for (int i = 0; i < bytes.length; i++) {
|
||||
b = bytes[i];
|
||||
if (b == '+') {
|
||||
buffer.write(CharUtil.SPACE);
|
||||
} else if (b == ESCAPE_CHAR) {
|
||||
if (i + 1 < bytes.length) {
|
||||
final int u = CharUtil.digit16(bytes[i + 1]);
|
||||
if (u >= 0 && i + 2 < bytes.length) {
|
||||
final int l = CharUtil.digit16(bytes[i + 2]);
|
||||
if (l >= 0) {
|
||||
buffer.write((char) ((u << 4) + l));
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 跳过不符合规范的%形式
|
||||
buffer.write(b);
|
||||
} else {
|
||||
buffer.write(b);
|
||||
}
|
||||
}
|
||||
return buffer.toByteArray();
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
package cn.hutool.core.net;
|
||||
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
@ -7,9 +10,6 @@ import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.BitSet;
|
||||
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
|
||||
/**
|
||||
* URL编码,数据内容的类型是 application/x-www-form-urlencoded。
|
||||
*
|
||||
@ -17,7 +17,6 @@ import cn.hutool.core.util.HexUtil;
|
||||
* 1.字符"a"-"z","A"-"Z","0"-"9",".","-","*",和"_" 都不会被编码;
|
||||
* 2.将空格转换为%20 ;
|
||||
* 3.将非文本内容转换成"%xy"的形式,xy是两位16进制的数值;
|
||||
* 4.在每个 name=value 对之间放置 & 符号。
|
||||
* </pre>
|
||||
*
|
||||
* @author looly,
|
||||
@ -196,10 +195,8 @@ public class URLEncoder implements Serializable{
|
||||
* @return 编码后的字符串
|
||||
*/
|
||||
public String encode(String path, Charset charset) {
|
||||
|
||||
int maxBytesPerChar = 10;
|
||||
final StringBuilder rewrittenPath = new StringBuilder(path.length());
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream(maxBytesPerChar);
|
||||
ByteArrayOutputStream buf = new ByteArrayOutputStream();
|
||||
OutputStreamWriter writer = new OutputStreamWriter(buf, charset);
|
||||
|
||||
int c;
|
||||
@ -221,9 +218,8 @@ public class URLEncoder implements Serializable{
|
||||
}
|
||||
|
||||
byte[] ba = buf.toByteArray();
|
||||
for (int j = 0; j < ba.length; j++) {
|
||||
for (byte toEncode : ba) {
|
||||
// Converting each byte in the buffer
|
||||
byte toEncode = ba[j];
|
||||
rewrittenPath.append('%');
|
||||
HexUtil.appendHex(rewrittenPath, toEncode, false);
|
||||
}
|
||||
|
495
hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java
Normal file
495
hutool-core/src/main/java/cn/hutool/core/net/url/UrlBuilder.java
Normal file
@ -0,0 +1,495 @@
|
||||
package cn.hutool.core.net.url;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
/**
|
||||
* URL 生成器,格式形如:
|
||||
* <pre>
|
||||
* [scheme:]scheme-specific-part[#fragment]
|
||||
* [scheme:][//authority][path][?query][#fragment]
|
||||
* [scheme:][//host:port][path][?query][#fragment]
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
* @see <a href="https://en.wikipedia.org/wiki/Uniform_Resource_Identifier">Uniform Resource Identifier</a>
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public final class UrlBuilder implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String DEFAULT_SCHEME = "http";
|
||||
|
||||
/**
|
||||
* 协议,例如http
|
||||
*/
|
||||
private String scheme;
|
||||
/**
|
||||
* 主机,例如127.0.0.1
|
||||
*/
|
||||
private String host;
|
||||
/**
|
||||
* 端口,默认-1
|
||||
*/
|
||||
private int port = -1;
|
||||
/**
|
||||
* 路径,例如/aa/bb/cc
|
||||
*/
|
||||
private UrlPath path;
|
||||
/**
|
||||
* 查询语句,例如a=1&b=2
|
||||
*/
|
||||
private UrlQuery query;
|
||||
/**
|
||||
* 标识符,例如#后边的部分
|
||||
*/
|
||||
private String fragment;
|
||||
|
||||
/**
|
||||
* 编码,用于URLEncode和URLDecode
|
||||
*/
|
||||
private Charset charset;
|
||||
|
||||
/**
|
||||
* 使用URI构建UrlBuilder
|
||||
*
|
||||
* @param uri URI
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder of(URI uri, Charset charset) {
|
||||
return of(uri.getScheme(), uri.getHost(), uri.getPort(), uri.getPath(), uri.getRawQuery(), uri.getFragment(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用URL字符串构建UrlBuilder
|
||||
*
|
||||
* @param httpUrl URL字符串
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder ofHttp(String httpUrl, Charset charset) {
|
||||
Assert.notBlank(httpUrl, "Http url must be not blank!");
|
||||
|
||||
final int sepIndex = httpUrl.indexOf("://");
|
||||
if (sepIndex < 0) {
|
||||
httpUrl = "http://" + httpUrl.trim();
|
||||
}
|
||||
return of(httpUrl, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用URL字符串构建UrlBuilder
|
||||
*
|
||||
* @param url URL字符串
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder of(String url, Charset charset) {
|
||||
Assert.notBlank(url, "Url must be not blank!");
|
||||
return of(URLUtil.url(url.trim()), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用URL构建UrlBuilder
|
||||
*
|
||||
* @param url URL
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder of(URL url, Charset charset) {
|
||||
return of(url.getProtocol(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef(), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建UrlBuilder
|
||||
*
|
||||
* @param scheme 协议,默认http
|
||||
* @param host 主机,例如127.0.0.1
|
||||
* @param port 端口,-1表示默认端口
|
||||
* @param path 路径,例如/aa/bb/cc
|
||||
* @param query 查询,例如a=1&b=2
|
||||
* @param fragment 标识符例如#后边的部分
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建UrlBuilder
|
||||
*
|
||||
* @param scheme 协议,默认http
|
||||
* @param host 主机,例如127.0.0.1
|
||||
* @param port 端口,-1表示默认端口
|
||||
* @param path 路径,例如/aa/bb/cc
|
||||
* @param query 查询,例如a=1&b=2
|
||||
* @param fragment 标识符例如#后边的部分
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder of(String scheme, String host, int port, UrlPath path, UrlQuery query, String fragment, Charset charset) {
|
||||
return new UrlBuilder(scheme, host, port, path, query, fragment, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建空的UrlBuilder
|
||||
*
|
||||
* @return UrlBuilder
|
||||
*/
|
||||
public static UrlBuilder create() {
|
||||
return new UrlBuilder();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public UrlBuilder() {
|
||||
this.charset = CharsetUtil.CHARSET_UTF_8;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param scheme 协议,默认http
|
||||
* @param host 主机,例如127.0.0.1
|
||||
* @param port 端口,-1表示默认端口
|
||||
* @param path 路径,例如/aa/bb/cc
|
||||
* @param query 查询,例如a=1&b=2
|
||||
* @param fragment 标识符例如#后边的部分
|
||||
* @param charset 编码,用于URLEncode和URLDecode
|
||||
*/
|
||||
public UrlBuilder(String scheme, String host, int port, UrlPath path, UrlQuery query, String fragment, Charset charset) {
|
||||
this.charset = charset;
|
||||
this.scheme = scheme;
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.path = path;
|
||||
this.query = query;
|
||||
this.setFragment(fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议,例如http
|
||||
*
|
||||
* @return 协议,例如http
|
||||
*/
|
||||
public String getScheme() {
|
||||
return scheme;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议,例如http,如果用户未定义协议,使用默认的http协议
|
||||
*
|
||||
* @return 协议,例如http
|
||||
*/
|
||||
public String getSchemeWithDefault() {
|
||||
return StrUtil.emptyToDefault(this.scheme, DEFAULT_SCHEME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置协议,例如http
|
||||
*
|
||||
* @param scheme 协议,例如http
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setScheme(String scheme) {
|
||||
this.scheme = scheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 主机,例如127.0.0.1
|
||||
*
|
||||
* @return 主机,例如127.0.0.1
|
||||
*/
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置主机,例如127.0.0.1
|
||||
*
|
||||
* @param host 主机,例如127.0.0.1
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setHost(String host) {
|
||||
this.host = host;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取端口,默认-1
|
||||
*
|
||||
* @return 端口,默认-1
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置端口,默认-1
|
||||
*
|
||||
* @param port 端口,默认-1
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setPort(int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得authority部分
|
||||
*
|
||||
* @return authority部分
|
||||
*/
|
||||
public String getAuthority() {
|
||||
return (port < 0) ? host : host + ":" + port;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路径,例如/aa/bb/cc
|
||||
*
|
||||
* @return 路径,例如/aa/bb/cc
|
||||
*/
|
||||
public UrlPath getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得路径,例如/aa/bb/cc
|
||||
*
|
||||
* @return 路径,例如/aa/bb/cc
|
||||
*/
|
||||
public String getPathStr() {
|
||||
return null == this.path ? StrUtil.SLASH : this.path.build(charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置路径,例如/aa/bb/cc,将覆盖之前所有的path相关设置
|
||||
*
|
||||
* @param path 路径,例如/aa/bb/cc
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setPath(UrlPath path) {
|
||||
this.path = path;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加路径节点
|
||||
*
|
||||
* @param segment 路径节点
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder addPath(String segment) {
|
||||
if (StrUtil.isBlank(segment)) {
|
||||
return this;
|
||||
}
|
||||
if (null == this.path) {
|
||||
this.path = new UrlPath();
|
||||
}
|
||||
this.path.add(segment);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加path节点
|
||||
*
|
||||
* @param segment path节点
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder appendPath(CharSequence segment) {
|
||||
if (StrUtil.isEmpty(segment)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.path == null) {
|
||||
this.path = new UrlPath();
|
||||
}
|
||||
this.path.add(segment);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询语句,例如a=1&b=2
|
||||
*
|
||||
* @return 查询语句,例如a=1&b=2
|
||||
*/
|
||||
public UrlQuery getQuery() {
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询语句,例如a=1&b=2
|
||||
*
|
||||
* @return 查询语句,例如a=1&b=2
|
||||
*/
|
||||
public String getQueryStr() {
|
||||
return null == this.query ? null : this.query.build(this.charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置查询语句,例如a=1&b=2,将覆盖之前所有的query相关设置
|
||||
*
|
||||
* @param query 查询语句,例如a=1&b=2
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setQuery(UrlQuery query) {
|
||||
this.query = query;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加查询项,支持重复键
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder addQuery(String key, String value) {
|
||||
if (StrUtil.isEmpty(key)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.query == null) {
|
||||
this.query = new UrlQuery();
|
||||
}
|
||||
this.query.add(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标识符,#后边的部分
|
||||
*
|
||||
* @return 标识符,例如#后边的部分
|
||||
*/
|
||||
public String getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标识符,#后边的部分
|
||||
*
|
||||
* @return 标识符,例如#后边的部分
|
||||
*/
|
||||
public String getFragmentEncoded() {
|
||||
return URLUtil.encodeAll(this.fragment, this.charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置标识符,例如#后边的部分
|
||||
*
|
||||
* @param fragment 标识符,例如#后边的部分
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setFragment(String fragment) {
|
||||
if (StrUtil.isEmpty(fragment)) {
|
||||
this.fragment = null;
|
||||
}
|
||||
this.fragment = StrUtil.removePrefix(fragment, "#");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取编码,用于URLEncode和URLDecode
|
||||
*
|
||||
* @return 编码
|
||||
*/
|
||||
public Charset getCharset() {
|
||||
return charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置编码,用于URLEncode和URLDecode
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return this
|
||||
*/
|
||||
public UrlBuilder setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建URL字符串
|
||||
*
|
||||
* @return URL字符串
|
||||
*/
|
||||
public String build() {
|
||||
return toURL().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为{@link URL} 对象
|
||||
*
|
||||
* @return {@link URL}
|
||||
*/
|
||||
public URL toURL() {
|
||||
return toURL(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为{@link URL} 对象
|
||||
*
|
||||
* @param handler {@link URLStreamHandler},null表示默认
|
||||
* @return {@link URL}
|
||||
*/
|
||||
public URL toURL(URLStreamHandler handler) {
|
||||
final StringBuilder fileBuilder = new StringBuilder();
|
||||
|
||||
// path
|
||||
fileBuilder.append(StrUtil.blankToDefault(getPathStr(), StrUtil.SLASH));
|
||||
|
||||
// query
|
||||
final String query = getQueryStr();
|
||||
if (StrUtil.isNotBlank(query)) {
|
||||
fileBuilder.append('?').append(query);
|
||||
}
|
||||
|
||||
// fragment
|
||||
if (StrUtil.isNotBlank(this.fragment)) {
|
||||
fileBuilder.append('#').append(getFragmentEncoded());
|
||||
}
|
||||
|
||||
try {
|
||||
return new URL(getSchemeWithDefault(), host, port, fileBuilder.toString(), handler);
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为URI
|
||||
*
|
||||
* @return URI
|
||||
*/
|
||||
public URI toURI() {
|
||||
try {
|
||||
return new URI(
|
||||
getSchemeWithDefault(),
|
||||
getAuthority(),
|
||||
getPathStr(),
|
||||
getQueryStr(),
|
||||
getFragmentEncoded());
|
||||
} catch (URISyntaxException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return build();
|
||||
}
|
||||
|
||||
}
|
172
hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java
Normal file
172
hutool-core/src/main/java/cn/hutool/core/net/url/UrlPath.java
Normal file
@ -0,0 +1,172 @@
|
||||
package cn.hutool.core.net.url;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
/**
|
||||
* URL中Path部分的封装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public class UrlPath {
|
||||
private List<String> segments;
|
||||
private boolean withEngTag;
|
||||
|
||||
/**
|
||||
* 构建UrlPath
|
||||
*
|
||||
* @param pathStr 初始化的路径字符串
|
||||
* @param charset decode用的编码,null表示不做decode
|
||||
* @return {@link UrlPath}
|
||||
*/
|
||||
public static UrlPath of(String pathStr, Charset charset) {
|
||||
final UrlPath urlPath = new UrlPath();
|
||||
urlPath.parse(pathStr, charset);
|
||||
return urlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否path的末尾加 /
|
||||
* @param withEngTag 是否path的末尾加 /
|
||||
* @return this
|
||||
*/
|
||||
public UrlPath setWithEndTag(boolean withEngTag){
|
||||
this.withEngTag = withEngTag;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取path的节点列表
|
||||
*
|
||||
* @return 节点列表
|
||||
*/
|
||||
public List<String> getSegments() {
|
||||
return this.segments;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得指定节点
|
||||
*
|
||||
* @param index 节点位置
|
||||
* @return 节点,无节点或者越界返回null
|
||||
*/
|
||||
public String getSegment(int index) {
|
||||
if (null == this.segments || index >= this.segments.size()) {
|
||||
return null;
|
||||
}
|
||||
return this.segments.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到path最后面
|
||||
*/
|
||||
public UrlPath add(CharSequence segment) {
|
||||
add(segment, false);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到path最前面
|
||||
*/
|
||||
public UrlPath addBefore(CharSequence segment) {
|
||||
add(segment, true);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析path
|
||||
*
|
||||
* @param path 路径,类似于aaa/bb/ccc
|
||||
* @param charset decode编码,null表示不解码
|
||||
* @return this
|
||||
*/
|
||||
public UrlPath parse(String path, Charset charset) {
|
||||
UrlPath urlPath = new UrlPath();
|
||||
|
||||
if (StrUtil.isNotEmpty(path)) {
|
||||
path = path.trim();
|
||||
|
||||
final StringTokenizer tokenizer = new StringTokenizer(path, "/");
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
add(URLUtil.decode(tokenizer.nextToken(), charset));
|
||||
}
|
||||
}
|
||||
|
||||
return urlPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建path,前面带'/'
|
||||
*
|
||||
* @param charset encode编码,null表示不做encode
|
||||
* @return 如果没有任何内容,则返回空字符串""
|
||||
*/
|
||||
public String build(Charset charset) {
|
||||
if (CollUtil.isEmpty(this.segments)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
for (String segment : segments) {
|
||||
builder.append(CharUtil.SLASH).append(URLUtil.encodeAll(segment, charset));
|
||||
}
|
||||
if(withEngTag || StrUtil.isEmpty(builder)){
|
||||
builder.append(CharUtil.SLASH);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return build(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加节点
|
||||
*
|
||||
* @param segment 节点
|
||||
* @param before 是否在前面添加
|
||||
*/
|
||||
private void add(CharSequence segment, boolean before) {
|
||||
final String seg = fixSegment(segment);
|
||||
if (null == seg) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (this.segments == null) {
|
||||
this.segments = new LinkedList<>();
|
||||
}
|
||||
if (before) {
|
||||
this.segments.add(0, seg);
|
||||
} else {
|
||||
this.segments.add(seg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修正节点,包括去掉前后的/,去掉空白符
|
||||
* @param segment 节点
|
||||
* @return 修正后的节点
|
||||
*/
|
||||
private static String fixSegment(CharSequence segment) {
|
||||
if (StrUtil.isEmpty(segment) || "/".contentEquals(segment)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String segmentStr = StrUtil.str(segment);
|
||||
segmentStr = StrUtil.trim(segmentStr);
|
||||
segmentStr = StrUtil.removePrefix(segmentStr, "/");
|
||||
segmentStr = StrUtil.removeSuffix(segmentStr, "/");
|
||||
segmentStr = StrUtil.trim(segmentStr);
|
||||
return segmentStr;
|
||||
}
|
||||
}
|
245
hutool-core/src/main/java/cn/hutool/core/net/url/UrlQuery.java
Normal file
245
hutool-core/src/main/java/cn/hutool/core/net/url/UrlQuery.java
Normal file
@ -0,0 +1,245 @@
|
||||
package cn.hutool.core.net.url;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.TableMap;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* URL中查询字符串部分的封装,类似于:
|
||||
* <pre>
|
||||
* key1=v1&key2=&key3=v3
|
||||
* </pre>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public class UrlQuery {
|
||||
|
||||
private final TableMap<CharSequence, CharSequence> query;
|
||||
|
||||
/**
|
||||
* 构建UrlQuery
|
||||
*
|
||||
* @param queryMap 初始化的查询键值对
|
||||
* @return {@link UrlQuery}
|
||||
*/
|
||||
public static UrlQuery of(Map<? extends CharSequence, ?> queryMap) {
|
||||
return new UrlQuery(queryMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建UrlQuery
|
||||
*
|
||||
* @param queryStr 初始化的查询字符串
|
||||
* @param charset decode用的编码,null表示不做decode
|
||||
* @return {@link UrlQuery}
|
||||
*/
|
||||
public static UrlQuery of(String queryStr, Charset charset) {
|
||||
final UrlQuery urlQuery = new UrlQuery();
|
||||
urlQuery.parse(queryStr, charset);
|
||||
return urlQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public UrlQuery() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param queryMap 初始化的查询键值对
|
||||
*/
|
||||
public UrlQuery(Map<? extends CharSequence, ?> queryMap) {
|
||||
if(MapUtil.isNotEmpty(queryMap)) {
|
||||
query = new TableMap<>(queryMap.size());
|
||||
addAll(queryMap);
|
||||
} else{
|
||||
query = new TableMap<>(MapUtil.DEFAULT_INITIAL_CAPACITY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加键值对
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值,集合和数组转换为逗号分隔形式
|
||||
* @return this
|
||||
*/
|
||||
public UrlQuery add(CharSequence key, Object value) {
|
||||
this.query.put(key, toStr(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量增加键值对
|
||||
*
|
||||
* @param queryMap query中的键值对
|
||||
* @return this
|
||||
*/
|
||||
public UrlQuery addAll(Map<? extends CharSequence, ?> queryMap) {
|
||||
if(MapUtil.isNotEmpty(queryMap)) {
|
||||
queryMap.forEach(this::add);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析URL中的查询字符串
|
||||
*
|
||||
* @param queryStr 查询字符串,类似于key1=v1&key2=&key3=v3
|
||||
* @param charset decode编码,null表示不做decode
|
||||
* @return this
|
||||
*/
|
||||
public UrlQuery parse(String queryStr, Charset charset) {
|
||||
if (StrUtil.isBlank(queryStr)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// 去掉Path部分
|
||||
int pathEndPos = queryStr.indexOf('?');
|
||||
if (pathEndPos > -1) {
|
||||
queryStr = StrUtil.subSuf(queryStr, pathEndPos + 1);
|
||||
if (StrUtil.isBlank(queryStr)) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
final int len = queryStr.length();
|
||||
String name = null;
|
||||
int pos = 0; // 未处理字符开始位置
|
||||
int i; // 未处理字符结束位置
|
||||
char c; // 当前字符
|
||||
for (i = 0; i < len; i++) {
|
||||
c = queryStr.charAt(i);
|
||||
if (c == '=') { // 键值对的分界点
|
||||
if (null == name) {
|
||||
// name可以是""
|
||||
name = queryStr.substring(pos, i);
|
||||
}
|
||||
pos = i + 1;
|
||||
} else if (c == '&') { // 参数对的分界点
|
||||
if (null == name && pos != i) {
|
||||
// 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
|
||||
addParam(queryStr.substring(pos, i), StrUtil.EMPTY, charset);
|
||||
} else if (name != null) {
|
||||
addParam(name, queryStr.substring(pos, i), charset);
|
||||
name = null;
|
||||
}
|
||||
pos = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结尾
|
||||
if (pos != i) {
|
||||
if (name == null) {
|
||||
addParam(queryStr.substring(pos, i), StrUtil.EMPTY, charset);
|
||||
} else {
|
||||
addParam(name, queryStr.substring(pos, i), charset);
|
||||
}
|
||||
} else if (name != null) {
|
||||
addParam(name, StrUtil.EMPTY, charset);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得查询的Map
|
||||
*
|
||||
* @return 查询的Map,只读
|
||||
*/
|
||||
public Map<CharSequence, CharSequence> getQueryMap(){
|
||||
return MapUtil.unmodifiable(this.query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取查询值
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
public CharSequence get(CharSequence key){
|
||||
if(MapUtil.isEmpty(this.query)){
|
||||
return null;
|
||||
}
|
||||
return this.query.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建URL查询字符串,即将key-value键值对转换为key1=v1&key2=&key3=v3形式
|
||||
*
|
||||
* @param charset encode编码,null表示不做encode编码
|
||||
* @return URL查询字符串
|
||||
*/
|
||||
public String build(Charset charset) {
|
||||
if (MapUtil.isEmpty(this.query)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
CharSequence key;
|
||||
CharSequence value;
|
||||
for (Map.Entry<CharSequence, CharSequence> entry : this.query) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
sb.append("&");
|
||||
}
|
||||
key = entry.getKey();
|
||||
if (StrUtil.isNotEmpty(key)) {
|
||||
sb.append(URLUtil.encodeAll(StrUtil.str(key), charset)).append("=");
|
||||
value = entry.getValue();
|
||||
if (StrUtil.isNotEmpty(value)) {
|
||||
sb.append(URLUtil.encodeAll(StrUtil.str(value), charset));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return build(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转换为字符串,用于URL的Query中
|
||||
*
|
||||
* @param value 值
|
||||
* @return 字符串
|
||||
*/
|
||||
private static String toStr(Object value) {
|
||||
String result;
|
||||
if (value instanceof Iterable) {
|
||||
result = CollUtil.join((Iterable<?>) value, ",");
|
||||
} else if (value instanceof Iterator) {
|
||||
result = IterUtil.join((Iterator<?>) value, ",");
|
||||
} else {
|
||||
result = Convert.toStr(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将键值对加入到值为List类型的Map中
|
||||
*
|
||||
* @param name key
|
||||
* @param value value
|
||||
* @param charset 编码
|
||||
*/
|
||||
private void addParam(String name, String value, Charset charset) {
|
||||
name = URLUtil.decode(name, charset);
|
||||
value = URLUtil.decode(value, charset);
|
||||
this.query.put(name, value);
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* URL相关工具
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.3.1
|
||||
*/
|
||||
package cn.hutool.core.net.url;
|
@ -332,4 +332,15 @@ public class CharUtil {
|
||||
public static int getType(int c) {
|
||||
return Character.getType(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取给定字符的16进制数值
|
||||
*
|
||||
* @param b 字符
|
||||
* @return 16进制字符
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public static int digit16(int b) {
|
||||
return Character.digit(b, 16);
|
||||
}
|
||||
}
|
||||
|
@ -2207,7 +2207,7 @@ public class StrUtil {
|
||||
if (ignoreCase) {
|
||||
return str1.toString().equalsIgnoreCase(str2.toString());
|
||||
} else {
|
||||
return str1.equals(str2);
|
||||
return str1.toString().contentEquals(str2);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,9 @@ import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.net.URLDecoder;
|
||||
import cn.hutool.core.net.URLEncoder;
|
||||
import cn.hutool.core.net.url.UrlQuery;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
@ -18,44 +20,69 @@ import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* 统一资源定位符相关工具类
|
||||
*
|
||||
* @author xiaoleilu
|
||||
*
|
||||
*/
|
||||
public class URLUtil {
|
||||
|
||||
/** 针对ClassPath路径的伪协议前缀(兼容Spring): "classpath:" */
|
||||
/**
|
||||
* 针对ClassPath路径的伪协议前缀(兼容Spring): "classpath:"
|
||||
*/
|
||||
public static final String CLASSPATH_URL_PREFIX = "classpath:";
|
||||
/** URL 前缀表示文件: "file:" */
|
||||
/**
|
||||
* URL 前缀表示文件: "file:"
|
||||
*/
|
||||
public static final String FILE_URL_PREFIX = "file:";
|
||||
/** URL 前缀表示jar: "jar:" */
|
||||
/**
|
||||
* URL 前缀表示jar: "jar:"
|
||||
*/
|
||||
public static final String JAR_URL_PREFIX = "jar:";
|
||||
/** URL 前缀表示war: "war:" */
|
||||
/**
|
||||
* URL 前缀表示war: "war:"
|
||||
*/
|
||||
public static final String WAR_URL_PREFIX = "war:";
|
||||
/** URL 协议表示文件: "file" */
|
||||
/**
|
||||
* URL 协议表示文件: "file"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_FILE = "file";
|
||||
/** URL 协议表示Jar文件: "jar" */
|
||||
/**
|
||||
* URL 协议表示Jar文件: "jar"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_JAR = "jar";
|
||||
/** URL 协议表示zip文件: "zip" */
|
||||
/**
|
||||
* URL 协议表示zip文件: "zip"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_ZIP = "zip";
|
||||
/** URL 协议表示WebSphere文件: "wsjar" */
|
||||
/**
|
||||
* URL 协议表示WebSphere文件: "wsjar"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_WSJAR = "wsjar";
|
||||
/** URL 协议表示JBoss zip文件: "vfszip" */
|
||||
/**
|
||||
* URL 协议表示JBoss zip文件: "vfszip"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_VFSZIP = "vfszip";
|
||||
/** URL 协议表示JBoss文件: "vfsfile" */
|
||||
/**
|
||||
* URL 协议表示JBoss文件: "vfsfile"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_VFSFILE = "vfsfile";
|
||||
/** URL 协议表示JBoss VFS资源: "vfs" */
|
||||
/**
|
||||
* URL 协议表示JBoss VFS资源: "vfs"
|
||||
*/
|
||||
public static final String URL_PROTOCOL_VFS = "vfs";
|
||||
/** Jar路径以及内部文件路径的分界符: "!/" */
|
||||
/**
|
||||
* Jar路径以及内部文件路径的分界符: "!/"
|
||||
*/
|
||||
public static final String JAR_URL_SEPARATOR = "!/";
|
||||
/** WAR路径及内部文件路径分界符 */
|
||||
/**
|
||||
* WAR路径及内部文件路径分界符
|
||||
*/
|
||||
public static final String WAR_URL_SEPARATOR = "*/";
|
||||
|
||||
/**
|
||||
@ -71,7 +98,7 @@ public class URLUtil {
|
||||
/**
|
||||
* 通过一个字符串形式的URL地址创建URL对象
|
||||
*
|
||||
* @param url URL
|
||||
* @param url URL
|
||||
* @param handler {@link URLStreamHandler}
|
||||
* @return URL对象
|
||||
* @since 4.1.1
|
||||
@ -111,7 +138,7 @@ public class URLUtil {
|
||||
/**
|
||||
* 将URL字符串转换为URL对象,并做必要验证
|
||||
*
|
||||
* @param urlStr URL字符串
|
||||
* @param urlStr URL字符串
|
||||
* @param handler {@link URLStreamHandler}
|
||||
* @return URL
|
||||
* @since 4.1.9
|
||||
@ -167,7 +194,7 @@ public class URLUtil {
|
||||
/**
|
||||
* 获得URL
|
||||
*
|
||||
* @param path 相对给定 class所在的路径
|
||||
* @param path 相对给定 class所在的路径
|
||||
* @param clazz 指定class
|
||||
* @return URL
|
||||
* @see ResourceUtil#getResource(String, Class)
|
||||
@ -181,7 +208,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param file URL对应的文件对象
|
||||
* @return URL
|
||||
* @exception UtilException MalformedURLException
|
||||
* @throws UtilException MalformedURLException
|
||||
*/
|
||||
public static URL getURL(File file) {
|
||||
Assert.notNull(file, "File is null !");
|
||||
@ -197,7 +224,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param files URL对应的文件对象
|
||||
* @return URL
|
||||
* @exception UtilException MalformedURLException
|
||||
* @throws UtilException MalformedURLException
|
||||
*/
|
||||
public static URL[] getURLs(File... files) {
|
||||
final URL[] urls = new URL[files.length];
|
||||
@ -219,8 +246,8 @@ public class URLUtil {
|
||||
* @return 域名的URI
|
||||
* @since 4.6.9
|
||||
*/
|
||||
public static URI getHost(URL url){
|
||||
if(null == url){
|
||||
public static URI getHost(URL url) {
|
||||
if (null == url) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -234,12 +261,26 @@ public class URLUtil {
|
||||
/**
|
||||
* 补全相对路径
|
||||
*
|
||||
* @param baseUrl 基准URL
|
||||
* @param baseUrl 基准URL
|
||||
* @param relativePath 相对URL
|
||||
* @return 相对路径
|
||||
* @exception UtilException MalformedURLException
|
||||
* @throws UtilException MalformedURLException
|
||||
* @deprecated 拼写错误,请使用{@link #completeUrl(String, String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static String complateUrl(String baseUrl, String relativePath) {
|
||||
return completeUrl(baseUrl, relativePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* 补全相对路径
|
||||
*
|
||||
* @param baseUrl 基准URL
|
||||
* @param relativePath 相对URL
|
||||
* @return 相对路径
|
||||
* @throws UtilException MalformedURLException
|
||||
*/
|
||||
public static String completeUrl(String baseUrl, String relativePath) {
|
||||
baseUrl = normalize(baseUrl, false);
|
||||
if (StrUtil.isBlank(baseUrl)) {
|
||||
return null;
|
||||
@ -260,7 +301,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param url URL
|
||||
* @return 编码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
*/
|
||||
public static String encodeAll(String url) {
|
||||
return encodeAll(url, CharsetUtil.CHARSET_UTF_8);
|
||||
@ -270,12 +311,15 @@ public class URLUtil {
|
||||
* 编码URL<br>
|
||||
* 将需要转换的内容(ASCII码形式之外的内容),用十六进制表示法转换出来,并在之前加上%开头。
|
||||
*
|
||||
* @param url URL
|
||||
* @param charset 编码
|
||||
* @param url URL
|
||||
* @param charset 编码,为null表示不编码
|
||||
* @return 编码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
*/
|
||||
public static String encodeAll(String url, Charset charset) throws UtilException {
|
||||
if (null == charset) {
|
||||
return url;
|
||||
}
|
||||
try {
|
||||
return java.net.URLEncoder.encode(url, charset.toString());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
@ -290,7 +334,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param url URL
|
||||
* @return 编码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public static String encode(String url) throws UtilException {
|
||||
@ -304,7 +348,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param url URL
|
||||
* @return 编码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public static String encodeQuery(String url) throws UtilException {
|
||||
@ -316,7 +360,7 @@ public class URLUtil {
|
||||
* 将需要转换的内容(ASCII码形式之外的内容),用十六进制表示法转换出来,并在之前加上%开头。<br>
|
||||
* 此方法用于URL自动编码,类似于浏览器中键入地址自动编码,对于像类似于“/”的字符不再编码
|
||||
*
|
||||
* @param url 被编码内容
|
||||
* @param url 被编码内容
|
||||
* @param charset 编码
|
||||
* @return 编码后的字符
|
||||
* @since 4.4.1
|
||||
@ -336,7 +380,7 @@ public class URLUtil {
|
||||
* 将需要转换的内容(ASCII码形式之外的内容),用十六进制表示法转换出来,并在之前加上%开头。<br>
|
||||
* 此方法用于POST请求中的请求体自动编码,转义大部分特殊字符
|
||||
*
|
||||
* @param url 被编码内容
|
||||
* @param url 被编码内容
|
||||
* @param charset 编码
|
||||
* @return 编码后的字符
|
||||
* @since 4.4.1
|
||||
@ -356,10 +400,10 @@ public class URLUtil {
|
||||
* 将需要转换的内容(ASCII码形式之外的内容),用十六进制表示法转换出来,并在之前加上%开头。<br>
|
||||
* 此方法用于URL自动编码,类似于浏览器中键入地址自动编码,对于像类似于“/”的字符不再编码
|
||||
*
|
||||
* @param url URL
|
||||
* @param url URL
|
||||
* @param charset 编码
|
||||
* @return 编码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
*/
|
||||
public static String encode(String url, String charset) throws UtilException {
|
||||
if (StrUtil.isEmpty(url)) {
|
||||
@ -373,10 +417,10 @@ public class URLUtil {
|
||||
* 将需要转换的内容(ASCII码形式之外的内容),用十六进制表示法转换出来,并在之前加上%开头。<br>
|
||||
* 此方法用于POST请求中的请求体自动编码,转义大部分特殊字符
|
||||
*
|
||||
* @param url URL
|
||||
* @param url URL
|
||||
* @param charset 编码
|
||||
* @return 编码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
*/
|
||||
public static String encodeQuery(String url, String charset) throws UtilException {
|
||||
return encodeQuery(url, StrUtil.isBlank(charset) ? CharsetUtil.defaultCharset() : CharsetUtil.charset(charset));
|
||||
@ -388,7 +432,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param url URL
|
||||
* @return 解码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public static String decode(String url) throws UtilException {
|
||||
@ -396,38 +440,32 @@ public class URLUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码application/x-www-form-urlencoded字符
|
||||
* 解码application/x-www-form-urlencoded字符<br>
|
||||
* 将%开头的16进制表示的内容解码。
|
||||
*
|
||||
* @param content 被解码内容
|
||||
* @param charset 编码
|
||||
* @param charset 编码,null表示不解码
|
||||
* @return 编码后的字符
|
||||
* @since 4.4.1
|
||||
*/
|
||||
public static String decode(String content, Charset charset) {
|
||||
if (null == charset) {
|
||||
charset = CharsetUtil.defaultCharset();
|
||||
return content;
|
||||
}
|
||||
return decode(content, charset.name());
|
||||
return URLDecoder.decode(content, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码URL<br>
|
||||
* 解码application/x-www-form-urlencoded字符<br>
|
||||
* 将%开头的16进制表示的内容解码。
|
||||
*
|
||||
* @param url URL
|
||||
* @param content URL
|
||||
* @param charset 编码
|
||||
* @return 解码后的URL
|
||||
* @exception UtilException UnsupportedEncodingException
|
||||
* @throws UtilException UnsupportedEncodingException
|
||||
*/
|
||||
public static String decode(String url, String charset) throws UtilException {
|
||||
if (StrUtil.isEmpty(url)) {
|
||||
return url;
|
||||
}
|
||||
try {
|
||||
return URLDecoder.decode(url, charset);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new UtilException(e, "Unsupported encoding: [{}]", charset);
|
||||
}
|
||||
public static String decode(String content, String charset) throws UtilException {
|
||||
return decode(content, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -435,7 +473,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param uriStr URI路径
|
||||
* @return path
|
||||
* @exception UtilException 包装URISyntaxException
|
||||
* @throws UtilException 包装URISyntaxException
|
||||
*/
|
||||
public static String getPath(String uriStr) {
|
||||
URI uri;
|
||||
@ -476,7 +514,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param url URL
|
||||
* @return URI
|
||||
* @exception UtilException 包装URISyntaxException
|
||||
* @throws UtilException 包装URISyntaxException
|
||||
*/
|
||||
public static URI toURI(URL url) throws UtilException {
|
||||
return toURI(url, false);
|
||||
@ -485,10 +523,10 @@ public class URLUtil {
|
||||
/**
|
||||
* 转URL为URI
|
||||
*
|
||||
* @param url URL
|
||||
* @param url URL
|
||||
* @param isEncode 是否编码参数中的特殊字符(默认UTF-8编码)
|
||||
* @return URI
|
||||
* @exception UtilException 包装URISyntaxException
|
||||
* @throws UtilException 包装URISyntaxException
|
||||
* @since 4.6.9
|
||||
*/
|
||||
public static URI toURI(URL url, boolean isEncode) throws UtilException {
|
||||
@ -504,7 +542,7 @@ public class URLUtil {
|
||||
*
|
||||
* @param location 字符串路径
|
||||
* @return URI
|
||||
* @exception UtilException 包装URISyntaxException
|
||||
* @throws UtilException 包装URISyntaxException
|
||||
*/
|
||||
public static URI toURI(String location) throws UtilException {
|
||||
return toURI(location, false);
|
||||
@ -516,11 +554,11 @@ public class URLUtil {
|
||||
* @param location 字符串路径
|
||||
* @param isEncode 是否编码参数中的特殊字符(默认UTF-8编码)
|
||||
* @return URI
|
||||
* @exception UtilException 包装URISyntaxException
|
||||
* @throws UtilException 包装URISyntaxException
|
||||
* @since 4.6.9
|
||||
*/
|
||||
public static URI toURI(String location, boolean isEncode) throws UtilException {
|
||||
if(isEncode){
|
||||
if (isEncode) {
|
||||
location = encode(location);
|
||||
}
|
||||
try {
|
||||
@ -590,7 +628,7 @@ public class URLUtil {
|
||||
/**
|
||||
* 获得Reader
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @param url {@link URL}
|
||||
* @param charset 编码
|
||||
* @return {@link BufferedReader}
|
||||
* @since 3.2.1
|
||||
@ -636,7 +674,7 @@ public class URLUtil {
|
||||
* 1. 多个/替换为一个
|
||||
* </pre>
|
||||
*
|
||||
* @param url URL字符串
|
||||
* @param url URL字符串
|
||||
* @param isEncodePath 是否对URL中path部分的中文和特殊字符做转义(不包括 http:, /和域名部分)
|
||||
* @return 标准化后的URL字符串
|
||||
* @since 4.4.1
|
||||
@ -663,7 +701,7 @@ public class URLUtil {
|
||||
body = StrUtil.subPre(body, paramsSepIndex);
|
||||
}
|
||||
|
||||
if(StrUtil.isNotEmpty(body)){
|
||||
if (StrUtil.isNotEmpty(body)) {
|
||||
// 去除开头的\或者/
|
||||
//noinspection ConstantConditions
|
||||
body = body.replaceAll("^[\\\\/]+", StrUtil.EMPTY);
|
||||
@ -683,4 +721,21 @@ public class URLUtil {
|
||||
}
|
||||
return protocol + domain + StrUtil.nullToEmpty(path) + StrUtil.nullToEmpty(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map形式的Form表单数据转换为Url参数形式<br>
|
||||
* paramMap中如果key为空(null和"")会被忽略,如果value为null,会被做为空白符("")<br>
|
||||
* 会自动url编码键和值
|
||||
*
|
||||
* <pre>
|
||||
* key1=v1&key2=&key3=v3
|
||||
* </pre>
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @param charset 编码,编码为null表示不编码
|
||||
* @return url参数
|
||||
*/
|
||||
public static String buildQuery(Map<String, ?> paramMap, Charset charset) {
|
||||
return UrlQuery.of(paramMap).build(charset);
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@ import cn.hutool.core.annotation.Alias;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.bean.copier.ValueProvider;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@ -154,7 +153,6 @@ public class BeanUtilTest {
|
||||
person.setSlow(true);
|
||||
|
||||
Map<String, Object> map = BeanUtil.beanToMap(person);
|
||||
Console.log(map);
|
||||
Assert.assertEquals("sub名字", map.get("aliasSubName"));
|
||||
}
|
||||
|
||||
|
173
hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java
Normal file
173
hutool-core/src/test/java/cn/hutool/core/net/UrlBuilderTest.java
Normal file
@ -0,0 +1,173 @@
|
||||
package cn.hutool.core.net;
|
||||
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class UrlBuilderTest {
|
||||
|
||||
@Test
|
||||
public void buildTest() {
|
||||
String buildUrl = UrlBuilder.create().setHost("www.baidu.com").build();
|
||||
Assert.assertEquals("http://www.baidu.com/", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHost() {
|
||||
String buildUrl = UrlBuilder.create()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com").build();
|
||||
Assert.assertEquals("https://www.baidu.com/", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHostPort() {
|
||||
String buildUrl = UrlBuilder.create()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.setPort(8080)
|
||||
.build();
|
||||
Assert.assertEquals("https://www.baidu.com:8080/", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPathAndQuery() {
|
||||
final String buildUrl = UrlBuilder.create()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.addPath("/aaa").addPath("bbb")
|
||||
.addQuery("ie", "UTF-8")
|
||||
.addQuery("wd", "test")
|
||||
.build();
|
||||
|
||||
Assert.assertEquals("https://www.baidu.com/aaa/bbb?ie=UTF-8&wd=test", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryWithChinese() {
|
||||
final String buildUrl = UrlBuilder.create()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.addPath("/aaa").addPath("bbb")
|
||||
.addQuery("ie", "UTF-8")
|
||||
.addQuery("wd", "测试")
|
||||
.build();
|
||||
|
||||
Assert.assertEquals("https://www.baidu.com/aaa/bbb?ie=UTF-8&wd=%E6%B5%8B%E8%AF%95", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiQueryWithChinese() {
|
||||
final String buildUrl = UrlBuilder.create()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.addPath("/s")
|
||||
.addQuery("ie", "UTF-8")
|
||||
.addQuery("ie", "GBK")
|
||||
.addQuery("wd", "测试")
|
||||
.build();
|
||||
|
||||
Assert.assertEquals("https://www.baidu.com/s?ie=UTF-8&ie=GBK&wd=%E6%B5%8B%E8%AF%95", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFragment() {
|
||||
String buildUrl = new UrlBuilder()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.setFragment("abc").build();
|
||||
Assert.assertEquals("https://www.baidu.com/#abc", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChineseFragment() {
|
||||
String buildUrl = new UrlBuilder()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.setFragment("测试").build();
|
||||
Assert.assertEquals("https://www.baidu.com/#%E6%B5%8B%E8%AF%95", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChineseFragmentWithPath() {
|
||||
String buildUrl = new UrlBuilder()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.addPath("/s")
|
||||
.setFragment("测试").build();
|
||||
Assert.assertEquals("https://www.baidu.com/s#%E6%B5%8B%E8%AF%95", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChineseFragmentWithPathAndQuery() {
|
||||
String buildUrl = new UrlBuilder()
|
||||
.setScheme("https")
|
||||
.setHost("www.baidu.com")
|
||||
.addPath("/s")
|
||||
.addQuery("wd", "test")
|
||||
.setFragment("测试").build();
|
||||
Assert.assertEquals("https://www.baidu.com/s?wd=test#%E6%B5%8B%E8%AF%95", buildUrl);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofTest() {
|
||||
final UrlBuilder builder = UrlBuilder.of("http://www.baidu.com/aaa/bbb/?a=1&b=2#frag1", CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals("http", builder.getScheme());
|
||||
Assert.assertEquals("www.baidu.com", builder.getHost());
|
||||
|
||||
Assert.assertEquals("aaa", builder.getPath().getSegment(0));
|
||||
Assert.assertEquals("bbb", builder.getPath().getSegment(1));
|
||||
|
||||
Assert.assertEquals("1", builder.getQuery().get("a"));
|
||||
Assert.assertEquals("2", builder.getQuery().get("b"));
|
||||
|
||||
Assert.assertEquals("frag1", builder.getFragment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofWithChineseTest() {
|
||||
final UrlBuilder builder = UrlBuilder.ofHttp("www.baidu.com/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals("http", builder.getScheme());
|
||||
Assert.assertEquals("www.baidu.com", builder.getHost());
|
||||
|
||||
Assert.assertEquals("aaa", builder.getPath().getSegment(0));
|
||||
Assert.assertEquals("bbb", builder.getPath().getSegment(1));
|
||||
|
||||
Assert.assertEquals("张三", builder.getQuery().get("a"));
|
||||
Assert.assertEquals("李四", builder.getQuery().get("b"));
|
||||
|
||||
Assert.assertEquals("frag1", builder.getFragment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofWithBlankTest() {
|
||||
final UrlBuilder builder = UrlBuilder.ofHttp(" www.baidu.com/aaa/bbb/?a=张三&b=%e6%9d%8e%e5%9b%9b#frag1", CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals("http", builder.getScheme());
|
||||
Assert.assertEquals("www.baidu.com", builder.getHost());
|
||||
|
||||
Assert.assertEquals("aaa", builder.getPath().getSegment(0));
|
||||
Assert.assertEquals("bbb", builder.getPath().getSegment(1));
|
||||
|
||||
Assert.assertEquals("张三", builder.getQuery().get("a"));
|
||||
Assert.assertEquals("李四", builder.getQuery().get("b"));
|
||||
|
||||
Assert.assertEquals("frag1", builder.getFragment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ofSpecialTest() {
|
||||
//测试不规范的或者无需解码的字符串是否成功解码
|
||||
final UrlBuilder builder = UrlBuilder.ofHttp(" www.baidu.com/aaa/bbb/?a=张三&b=%%e5%9b%9b#frag1", CharsetUtil.CHARSET_UTF_8);
|
||||
Assert.assertEquals("http", builder.getScheme());
|
||||
Assert.assertEquals("www.baidu.com", builder.getHost());
|
||||
|
||||
Assert.assertEquals("aaa", builder.getPath().getSegment(0));
|
||||
Assert.assertEquals("bbb", builder.getPath().getSegment(1));
|
||||
|
||||
Assert.assertEquals("张三", builder.getQuery().get("a"));
|
||||
Assert.assertEquals("%四", builder.getQuery().get("b"));
|
||||
|
||||
Assert.assertEquals("frag1", builder.getFragment());
|
||||
}
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
package cn.hutool.crypto.symmetric;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.Mode;
|
||||
import cn.hutool.crypto.Padding;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
||||
/**
|
||||
* AES加密算法实现<br>
|
||||
* 高级加密标准(英语:Advanced Encryption Standard,缩写:AES),在密码学中又称Rijndael加密法<br>
|
||||
@ -105,7 +105,7 @@ public class AES extends SymmetricCrypto {
|
||||
* @since 4.6.7
|
||||
*/
|
||||
public AES(Mode mode, Padding padding, SecretKey key, byte[] iv) {
|
||||
this(mode, padding, key, ArrayUtil.isEmpty(iv) ? ((IvParameterSpec) null) : new IvParameterSpec(iv));
|
||||
this(mode, padding, key, ArrayUtil.isEmpty(iv) ? null : new IvParameterSpec(iv));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -153,7 +153,7 @@ public class AES extends SymmetricCrypto {
|
||||
public AES(String mode, String padding, byte[] key, byte[] iv) {
|
||||
this(mode, padding,//
|
||||
SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue(), key),//
|
||||
ArrayUtil.isEmpty(iv) ? ((IvParameterSpec) null) : new IvParameterSpec(iv));
|
||||
ArrayUtil.isEmpty(iv) ? null : new IvParameterSpec(iv));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,11 +12,11 @@ import cn.hutool.core.io.resource.MultiResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.net.url.UrlBuilder;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.cookie.GlobalCookieManager;
|
||||
import cn.hutool.http.ssl.SSLSocketFactoryBuilder;
|
||||
|
||||
@ -96,7 +96,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
GlobalCookieManager.setCookieManager(null);
|
||||
}
|
||||
|
||||
private String url;
|
||||
private UrlBuilder url;
|
||||
private URLStreamHandler urlHandler;
|
||||
private Method method = Method.GET;
|
||||
/**
|
||||
@ -128,10 +128,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
* 是否禁用缓存
|
||||
*/
|
||||
private boolean isDisableCache;
|
||||
/**
|
||||
* 是否对url中的参数进行编码
|
||||
*/
|
||||
private boolean encodeUrlParams;
|
||||
/**
|
||||
* 是否是REST请求模式
|
||||
*/
|
||||
@ -168,8 +164,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
* @param url URL
|
||||
*/
|
||||
public HttpRequest(String url) {
|
||||
Assert.notBlank(url, "Param [url] can not be blank !");
|
||||
this.url = URLUtil.normalize(url, true);
|
||||
setUrl(url);
|
||||
// 给定一个默认头信息
|
||||
this.header(GlobalHeaders.INSTANCE.headers);
|
||||
}
|
||||
@ -265,7 +260,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
* @since 4.1.8
|
||||
*/
|
||||
public String getUrl() {
|
||||
return url;
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -276,7 +271,19 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
* @since 4.1.8
|
||||
*/
|
||||
public HttpRequest setUrl(String url) {
|
||||
this.url = url;
|
||||
this.url = UrlBuilder.ofHttp(url, this.charset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置URL
|
||||
*
|
||||
* @param urlBuilder url字符串
|
||||
* @return this
|
||||
* @since 5.3.1
|
||||
*/
|
||||
public HttpRequest setUrl(UrlBuilder urlBuilder) {
|
||||
this.url = urlBuilder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -774,9 +781,10 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
* @param isEncodeUrlParams 是否对URL中的参数进行编码
|
||||
* @return this
|
||||
* @since 4.4.1
|
||||
* @deprecated 编码自动完成,无需设置
|
||||
*/
|
||||
@Deprecated
|
||||
public HttpRequest setEncodeUrlParams(boolean isEncodeUrlParams) {
|
||||
this.encodeUrlParams = isEncodeUrlParams;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -925,10 +933,6 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
public HttpResponse execute(boolean isAsync) {
|
||||
// 初始化URL
|
||||
urlWithParamIfGet();
|
||||
// 编码URL
|
||||
if (this.encodeUrlParams) {
|
||||
this.url = HttpUtil.encodeParams(this.url, this.charset);
|
||||
}
|
||||
|
||||
// 初始化 connection
|
||||
initConnection();
|
||||
@ -982,7 +986,8 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
this.httpConnection.disconnectQuietly();
|
||||
}
|
||||
|
||||
this.httpConnection = HttpConnection.create(URLUtil.toUrlForHttp(this.url, this.urlHandler), this.proxy)//
|
||||
this.httpConnection = HttpConnection
|
||||
.create(this.url.toURL(this.urlHandler), this.proxy)//
|
||||
.setMethod(this.method)//
|
||||
.setHttpsInfo(this.hostnameVerifier, this.ssf)//
|
||||
.setConnectTimeout(this.connectionTimeout)//
|
||||
@ -1016,9 +1021,9 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
if (Method.GET.equals(method) && false == this.isRest) {
|
||||
// 优先使用body形式的参数,不存在使用form
|
||||
if (ArrayUtil.isNotEmpty(this.bodyBytes)) {
|
||||
this.url = HttpUtil.urlWithForm(this.url, StrUtil.str(this.bodyBytes, this.charset), this.charset, false);
|
||||
this.url.getQuery().parse(StrUtil.str(this.bodyBytes, this.charset), this.charset);
|
||||
} else {
|
||||
this.url = HttpUtil.urlWithForm(this.url, this.form, this.charset, false);
|
||||
this.url.getQuery().addAll(this.form);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1047,7 +1052,7 @@ public class HttpRequest extends HttpBase<HttpRequest> {
|
||||
|
||||
if (responseCode != HttpURLConnection.HTTP_OK) {
|
||||
if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_SEE_OTHER) {
|
||||
this.url = httpConnection.header(Header.LOCATION);
|
||||
setUrl(httpConnection.header(Header.LOCATION));
|
||||
if (redirectCount < this.maxRedirectCount) {
|
||||
redirectCount++;
|
||||
return execute();
|
||||
|
@ -471,11 +471,11 @@ public class HttpResponse extends HttpBase<HttpResponse> implements Closeable {
|
||||
*/
|
||||
private String getFileNameFromDisposition() {
|
||||
String fileName = null;
|
||||
final String desposition = header(Header.CONTENT_DISPOSITION);
|
||||
if (StrUtil.isNotBlank(desposition)) {
|
||||
fileName = ReUtil.get("filename=\"(.*?)\"", desposition, 1);
|
||||
final String disposition = header(Header.CONTENT_DISPOSITION);
|
||||
if (StrUtil.isNotBlank(disposition)) {
|
||||
fileName = ReUtil.get("filename=\"(.*?)\"", disposition, 1);
|
||||
if (StrUtil.isBlank(fileName)) {
|
||||
fileName = StrUtil.subAfter(desposition, "filename=", true);
|
||||
fileName = StrUtil.subAfter(disposition, "filename=", true);
|
||||
}
|
||||
}
|
||||
return fileName;
|
||||
|
@ -1,14 +1,12 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.collection.IterUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.FastByteArrayOutputStream;
|
||||
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.UrlQuery;
|
||||
import cn.hutool.core.text.StrBuilder;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
@ -23,13 +21,9 @@ import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@ -390,44 +384,11 @@ public class HttpUtil {
|
||||
* </pre>
|
||||
*
|
||||
* @param paramMap 表单数据
|
||||
* @param charset 编码
|
||||
* @param charset 编码,null表示不encode键值对
|
||||
* @return url参数
|
||||
*/
|
||||
public static String toParams(Map<String, ?> paramMap, Charset charset) {
|
||||
if (CollectionUtil.isEmpty(paramMap)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
if (null == charset) {// 默认编码为系统编码
|
||||
charset = CharsetUtil.CHARSET_UTF_8;
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
String key;
|
||||
Object value;
|
||||
String valueStr;
|
||||
for (Entry<String, ?> item : paramMap.entrySet()) {
|
||||
if (isFirst) {
|
||||
isFirst = false;
|
||||
} else {
|
||||
sb.append("&");
|
||||
}
|
||||
key = item.getKey();
|
||||
value = item.getValue();
|
||||
if (value instanceof Iterable) {
|
||||
value = CollUtil.join((Iterable<?>) value, ",");
|
||||
} else if (value instanceof Iterator) {
|
||||
value = IterUtil.join((Iterator<?>) value, ",");
|
||||
}
|
||||
valueStr = Convert.toStr(value);
|
||||
if (StrUtil.isNotEmpty(key)) {
|
||||
sb.append(URLUtil.encodeAll(key, charset)).append("=");
|
||||
if (StrUtil.isNotEmpty(valueStr)) {
|
||||
sb.append(URLUtil.encodeAll(valueStr, charset));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
return URLUtil.buildQuery(paramMap, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -437,7 +398,7 @@ public class HttpUtil {
|
||||
* <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
|
||||
*
|
||||
* @param urlWithParams url和参数,可以包含url本身,也可以单独参数
|
||||
* @param charset 编码
|
||||
* @param charset 编码
|
||||
* @return 编码后的url和参数
|
||||
* @since 4.0.1
|
||||
*/
|
||||
@ -457,10 +418,10 @@ public class HttpUtil {
|
||||
// 无参数,返回url
|
||||
return urlPart;
|
||||
}
|
||||
} else if(false == StrUtil.contains(urlWithParams, '=')){
|
||||
} else if (false == StrUtil.contains(urlWithParams, '=')) {
|
||||
// 无参数的URL
|
||||
return urlWithParams;
|
||||
}else {
|
||||
} else {
|
||||
// 无URL的参数
|
||||
paramPart = urlWithParams;
|
||||
}
|
||||
@ -536,8 +497,10 @@ public class HttpUtil {
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
* @since 4.0.2
|
||||
* @deprecated 请使用 {@link #decodeParamMap(String, Charset)}
|
||||
*/
|
||||
public static HashMap<String, String> decodeParamMap(String paramsStr, String charset) {
|
||||
@Deprecated
|
||||
public static Map<String, String> decodeParamMap(String paramsStr, String charset) {
|
||||
return decodeParamMap(paramsStr, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
@ -549,15 +512,12 @@ public class HttpUtil {
|
||||
* @return 参数Map
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static HashMap<String, String> decodeParamMap(String paramsStr, Charset charset) {
|
||||
final Map<String, List<String>> paramsMap = decodeParams(paramsStr, charset);
|
||||
final HashMap<String, String> result = MapUtil.newHashMap(paramsMap.size());
|
||||
List<String> valueList;
|
||||
for (Entry<String, List<String>> entry : paramsMap.entrySet()) {
|
||||
valueList = entry.getValue();
|
||||
result.put(entry.getKey(), CollUtil.isEmpty(valueList) ? null : valueList.get(0));
|
||||
public static Map<String, String> decodeParamMap(String paramsStr, Charset charset) {
|
||||
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||
if (MapUtil.isEmpty(queryMap)) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
return result;
|
||||
return Convert.toMap(String.class, String.class, queryMap);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -580,56 +540,17 @@ public class HttpUtil {
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Map<String, List<String>> decodeParams(String paramsStr, Charset charset) {
|
||||
if (StrUtil.isBlank(paramsStr)) {
|
||||
return Collections.emptyMap();
|
||||
final Map<CharSequence, CharSequence> queryMap = UrlQuery.of(paramsStr, charset).getQueryMap();
|
||||
if (MapUtil.isEmpty(queryMap)) {
|
||||
return MapUtil.empty();
|
||||
}
|
||||
|
||||
// 去掉Path部分
|
||||
int pathEndPos = paramsStr.indexOf('?');
|
||||
if (pathEndPos > -1) {
|
||||
paramsStr = StrUtil.subSuf(paramsStr, pathEndPos + 1);
|
||||
if (StrUtil.isBlank(paramsStr)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
}
|
||||
|
||||
final int len = paramsStr.length();
|
||||
final Map<String, List<String>> params = new LinkedHashMap<>();
|
||||
String name = null;
|
||||
int pos = 0; // 未处理字符开始位置
|
||||
int i; // 未处理字符结束位置
|
||||
char c; // 当前字符
|
||||
for (i = 0; i < len; i++) {
|
||||
c = paramsStr.charAt(i);
|
||||
if (c == '=') { // 键值对的分界点
|
||||
if (null == name) {
|
||||
// name可以是""
|
||||
name = paramsStr.substring(pos, i);
|
||||
}
|
||||
pos = i + 1;
|
||||
} else if (c == '&') { // 参数对的分界点
|
||||
if (null == name && pos != i) {
|
||||
// 对于像&a&这类无参数值的字符串,我们将name为a的值设为""
|
||||
addParam(params, paramsStr.substring(pos, i), StrUtil.EMPTY, charset);
|
||||
} else if (name != null) {
|
||||
addParam(params, name, paramsStr.substring(pos, i), charset);
|
||||
name = null;
|
||||
}
|
||||
pos = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理结尾
|
||||
if (pos != i) {
|
||||
if (name == null) {
|
||||
addParam(params, paramsStr.substring(pos, i), StrUtil.EMPTY, charset);
|
||||
} else {
|
||||
addParam(params, name, paramsStr.substring(pos, i), charset);
|
||||
}
|
||||
} else if (name != null) {
|
||||
addParam(params, name, StrUtil.EMPTY, charset);
|
||||
}
|
||||
|
||||
queryMap.forEach((key, value) -> {
|
||||
final List<String> values = params.computeIfAbsent(StrUtil.str(key), k -> new ArrayList<>(1));
|
||||
// 一般是一个参数
|
||||
values.add(StrUtil.str(value));
|
||||
});
|
||||
return params;
|
||||
}
|
||||
|
||||
@ -826,26 +747,7 @@ public class HttpUtil {
|
||||
* @return {@link SimpleServer}
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static SimpleServer createServer(int port){
|
||||
public static SimpleServer createServer(int port) {
|
||||
return new SimpleServer(port);
|
||||
}
|
||||
// ----------------------------------------------------------------------------------------- Private method start
|
||||
|
||||
/**
|
||||
* 将键值对加入到值为List类型的Map中
|
||||
*
|
||||
* @param params 参数
|
||||
* @param name key
|
||||
* @param value value
|
||||
* @param charset 编码
|
||||
*/
|
||||
private static void addParam(Map<String, List<String>> params, String name, String value, Charset charset) {
|
||||
name = URLUtil.decode(name, charset);
|
||||
value = URLUtil.decode(value, charset);
|
||||
final List<String> values = params.computeIfAbsent(name, k -> new ArrayList<>(1));
|
||||
// 一般是一个参数
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------- Private method start end
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ 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;
|
||||
@ -18,7 +19,7 @@ import java.util.Map;
|
||||
public class HttpUtilTest {
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// @Ignore
|
||||
public void postTest() {
|
||||
String result = HttpUtil.createPost("api.uhaozu.com/goods/description/1120448506").charset(CharsetUtil.UTF_8).execute().body();
|
||||
Console.log(result);
|
||||
@ -71,6 +72,17 @@ public class HttpUtilTest {
|
||||
Console.log(str);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void getTest5() {
|
||||
String url2 = "http://storage.chancecloud.com.cn/20200413_%E7%B2%A4B12313_386.pdf";
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user