responseInterceptors = GlobalInterceptor.INSTANCE.getCopiedResponseInterceptor();
+
+ /**
+ * 重定向时是否使用拦截器
+ */
+ boolean interceptorOnRedirect;
+
+ /**
+ * 设置超时,单位:毫秒
+ * 超时包括:
+ *
+ *
+ * 1. 连接超时
+ * 2. 读取响应超时
+ *
+ *
+ * @param milliseconds 超时毫秒数
+ * @return this
+ * @see #setConnectionTimeout(int)
+ * @see #setReadTimeout(int)
+ */
+ public HttpConfig timeout(int milliseconds) {
+ setConnectionTimeout(milliseconds);
+ setReadTimeout(milliseconds);
+ return this;
+ }
+
+ /**
+ * 设置连接超时,单位:毫秒
+ *
+ * @param milliseconds 超时毫秒数
+ * @return this
+ */
+ public HttpConfig setConnectionTimeout(int milliseconds) {
+ this.connectionTimeout = milliseconds;
+ return this;
+ }
+
+ /**
+ * 设置连接超时,单位:毫秒
+ *
+ * @param milliseconds 超时毫秒数
+ * @return this
+ */
+ public HttpConfig setReadTimeout(int milliseconds) {
+ this.readTimeout = milliseconds;
+ return this;
+ }
+
+ /**
+ * 禁用缓存
+ *
+ * @return this
+ */
+ public HttpConfig disableCache() {
+ this.isDisableCache = true;
+ return this;
+ }
+
+ /**
+ * 设置最大重定向次数
+ * 如果次数小于1则表示不重定向,大于等于1表示打开重定向
+ *
+ * @param maxRedirectCount 最大重定向次数
+ * @return this
+ */
+ public HttpConfig setMaxRedirectCount(int maxRedirectCount) {
+ this.maxRedirectCount = Math.max(maxRedirectCount, 0);
+ return this;
+ }
+
+ /**
+ * 设置域名验证器
+ * 只针对HTTPS请求,如果不设置,不做验证,所有域名被信任
+ *
+ * @param hostnameVerifier HostnameVerifier
+ * @return this
+ */
+ public HttpConfig setHostnameVerifier(HostnameVerifier hostnameVerifier) {
+ // 验证域
+ this.hostnameVerifier = hostnameVerifier;
+ return this;
+ }
+
+ /**
+ * 设置Http代理
+ *
+ * @param host 代理 主机
+ * @param port 代理 端口
+ * @return this
+ */
+ public HttpConfig setHttpProxy(String host, int port) {
+ final Proxy proxy = new Proxy(Proxy.Type.HTTP,
+ new InetSocketAddress(host, port));
+ return setProxy(proxy);
+ }
+
+ /**
+ * 设置代理
+ *
+ * @param proxy 代理 {@link Proxy}
+ * @return this
+ */
+ public HttpConfig setProxy(Proxy proxy) {
+ this.proxy = proxy;
+ return this;
+ }
+
+ /**
+ * 设置SSLSocketFactory
+ * 只针对HTTPS请求,如果不设置,使用默认的SSLSocketFactory
+ * 默认SSLSocketFactory为:SSLSocketFactoryBuilder.create().build();
+ *
+ * @param ssf SSLScketFactory
+ * @return this
+ */
+ public HttpConfig setSSLSocketFactory(SSLSocketFactory ssf) {
+ this.ssf = ssf;
+ return this;
+ }
+
+ /**
+ * 设置HTTPS安全连接协议,只针对HTTPS请求,可以使用的协议包括:
+ * 此方法调用后{@link #setSSLSocketFactory(SSLSocketFactory)} 将被覆盖。
+ *
+ *
+ * 1. TLSv1.2
+ * 2. TLSv1.1
+ * 3. SSLv3
+ * ...
+ *
+ *
+ * @param protocol 协议
+ * @return this
+ * @see SSLUtil#createSSLContext(String)
+ * @see #setSSLSocketFactory(SSLSocketFactory)
+ */
+ public HttpConfig setSSLProtocol(String protocol) {
+ Assert.notBlank(protocol, "protocol must be not blank!");
+ setSSLSocketFactory(SSLUtil.createSSLContext(protocol).getSocketFactory());
+ return this;
+ }
+
+ /**
+ * 采用流方式上传数据,无需本地缓存数据。
+ * HttpUrlConnection默认是将所有数据读到本地缓存,然后再发送给服务器,这样上传大文件时就会导致内存溢出。
+ *
+ * @param blockSize 块大小(bytes数),0或小于0表示不设置Chuncked模式
+ * @return this
+ */
+ public HttpConfig setBlockSize(int blockSize) {
+ this.blockSize = blockSize;
+ return this;
+ }
+
+ /**
+ * 设置是否忽略响应读取时可能的EOF异常。
+ * 在Http协议中,对于Transfer-Encoding: Chunked在正常情况下末尾会写入一个Length为0的的chunk标识完整结束。
+ * 如果服务端未遵循这个规范或响应没有正常结束,会报EOF异常,此选项用于是否忽略这个异常。
+ *
+ * @param ignoreEOFError 是否忽略响应读取时可能的EOF异常。
+ * @since 5.7.20
+ */
+ public HttpConfig setIgnoreEOFError(boolean ignoreEOFError) {
+ this.ignoreEOFError = ignoreEOFError;
+ return this;
+ }
+
+ /**
+ * 设置是否忽略解码URL,包括URL中的Path部分和Param部分。
+ * 在构建Http请求时,用户传入的URL可能有编码后和未编码的内容混合在一起,如果此参数为{@code true},则会统一解码编码后的参数,
+ * 按照RFC3986规范,在发送请求时,全部编码之。如果为{@code false},则不会解码已经编码的内容,在请求时只编码需要编码的部分。
+ *
+ * @param decodeUrl 是否忽略解码URL
+ */
+ public HttpConfig setDecodeUrl(boolean decodeUrl) {
+ this.decodeUrl = decodeUrl;
+ return this;
+ }
+
+ /**
+ * 设置拦截器,用于在请求前重新编辑请求
+ *
+ * @param interceptor 拦截器实现
+ */
+ public HttpConfig addRequestInterceptor(HttpInterceptor interceptor) {
+ this.requestInterceptors.addChain(interceptor);
+ return this;
+ }
+
+ /**
+ * 设置拦截器,用于在请求前重新编辑请求
+ *
+ * @param interceptor 拦截器实现
+ */
+ public HttpConfig addResponseInterceptor(HttpInterceptor interceptor) {
+ this.responseInterceptors.addChain(interceptor);
+ return this;
+ }
+
+ /**
+ * 重定向时是否使用拦截器
+ *
+ * @param interceptorOnRedirect 重定向时是否使用拦截器
+ * @return this
+ */
+ public HttpConfig setInterceptorOnRedirect(boolean interceptorOnRedirect) {
+ this.interceptorOnRedirect = interceptorOnRedirect;
+ return this;
+ }
+}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java b/hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java
index 6e754f9cf..246d7f37d 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpInterceptor.java
@@ -5,38 +5,40 @@ import java.util.LinkedList;
import java.util.List;
/**
- * Http拦截器接口,通过实现此接口,完成请求发起前对请求的编辑工作
+ * Http拦截器接口,通过实现此接口,完成请求发起前或结束后对请求的编辑工作
*
+ * @param 过滤参数类型,HttpRequest或者HttpResponse
* @author looly
* @since 5.7.16
*/
@FunctionalInterface
-public interface HttpInterceptor {
+public interface HttpInterceptor> {
/**
* 处理请求
*
- * @param request 请求
+ * @param httpObj 请求或响应对象
*/
- void process(HttpRequest request);
+ void process(T httpObj);
/**
* 拦截器链
*
+ * @param 过滤参数类型,HttpRequest或者HttpResponse
* @author looly
* @since 5.7.16
*/
- class Chain implements cn.hutool.core.lang.Chain {
- private final List interceptors = new LinkedList<>();
+ class Chain> implements cn.hutool.core.lang.Chain, Chain> {
+ private final List> interceptors = new LinkedList<>();
@Override
- public Chain addChain(HttpInterceptor element) {
+ public Chain addChain(HttpInterceptor element) {
interceptors.add(element);
return this;
}
@Override
- public Iterator iterator() {
+ public Iterator> iterator() {
return interceptors.iterator();
}
@@ -46,7 +48,7 @@ public interface HttpInterceptor {
* @return this
* @since 5.8.0
*/
- public Chain clear() {
+ public Chain clear() {
interceptors.clear();
return this;
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
index aefce9c3b..cb56e4b00 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpRequest.java
@@ -28,7 +28,6 @@ import java.io.IOException;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URLStreamHandler;
import java.nio.charset.Charset;
@@ -200,43 +199,27 @@ public class HttpRequest extends HttpBase {
}
// ---------------------------------------------------------------- static Http Method end
+ private HttpConfig config = HttpConfig.create();
private UrlBuilder url;
private URLStreamHandler urlHandler;
private Method method = Method.GET;
/**
- * 请求前的拦截器,用于在请求前重新编辑请求
+ * 连接对象
*/
- private final HttpInterceptor.Chain interceptors = GlobalInterceptor.INSTANCE.getCopied();
+ private HttpConnection httpConnection;
- /**
- * 默认连接超时
- */
- private int connectionTimeout = HttpGlobalConfig.getTimeout();
- /**
- * 默认读取超时
- */
- private int readTimeout = HttpGlobalConfig.getTimeout();
/**
* 存储表单数据
*/
private Map form;
- /**
- * 是否为Multipart表单
- */
- private boolean isMultiPart;
/**
* Cookie
*/
private String cookie;
-
/**
- * 连接对象
+ * 是否为Multipart表单
*/
- private HttpConnection httpConnection;
- /**
- * 是否禁用缓存
- */
- private boolean isDisableCache;
+ private boolean isMultiPart;
/**
* 是否是REST请求模式
*/
@@ -245,27 +228,6 @@ public class HttpRequest extends HttpBase {
* 重定向次数计数器,内部使用
*/
private int redirectCount;
- /**
- * 最大重定向次数
- */
- private int maxRedirectCount = HttpGlobalConfig.getMaxRedirectCount();
- /**
- * Chuncked块大小,0或小于0表示不设置Chuncked模式
- */
- private int blockSize;
- /**
- * 代理
- */
- private Proxy proxy;
-
- /**
- * HostnameVerifier,用于HTTPS安全连接
- */
- private HostnameVerifier hostnameVerifier;
- /**
- * SSLSocketFactory,用于HTTPS安全连接
- */
- private SSLSocketFactory ssf;
/**
* 构造,URL编码默认使用UTF-8
@@ -784,6 +746,18 @@ public class HttpRequest extends HttpBase {
}
// ---------------------------------------------------------------- Body end
+ /**
+ * 将新的配置加入
+ * 注意加入的配置可能被修改
+ *
+ * @param config 配置
+ * @return this
+ */
+ public HttpRequest setConfig(HttpConfig config){
+ this.config = config;
+ return this;
+ }
+
/**
* 设置超时,单位:毫秒
* 超时包括:
@@ -799,8 +773,7 @@ public class HttpRequest extends HttpBase {
* @see #setReadTimeout(int)
*/
public HttpRequest timeout(int milliseconds) {
- setConnectionTimeout(milliseconds);
- setReadTimeout(milliseconds);
+ config.timeout(milliseconds);
return this;
}
@@ -812,7 +785,7 @@ public class HttpRequest extends HttpBase {
* @since 4.5.6
*/
public HttpRequest setConnectionTimeout(int milliseconds) {
- this.connectionTimeout = milliseconds;
+ config.setConnectionTimeout(milliseconds);
return this;
}
@@ -824,7 +797,7 @@ public class HttpRequest extends HttpBase {
* @since 4.5.6
*/
public HttpRequest setReadTimeout(int milliseconds) {
- this.readTimeout = milliseconds;
+ config.setReadTimeout(milliseconds);
return this;
}
@@ -834,7 +807,7 @@ public class HttpRequest extends HttpBase {
* @return this
*/
public HttpRequest disableCache() {
- this.isDisableCache = true;
+ config.disableCache();
return this;
}
@@ -858,7 +831,7 @@ public class HttpRequest extends HttpBase {
* @since 3.3.0
*/
public HttpRequest setMaxRedirectCount(int maxRedirectCount) {
- this.maxRedirectCount = Math.max(maxRedirectCount, 0);
+ config.setMaxRedirectCount(maxRedirectCount);
return this;
}
@@ -870,8 +843,7 @@ public class HttpRequest extends HttpBase {
* @return this
*/
public HttpRequest setHostnameVerifier(HostnameVerifier hostnameVerifier) {
- // 验证域
- this.hostnameVerifier = hostnameVerifier;
+ config.setHostnameVerifier(hostnameVerifier);
return this;
}
@@ -884,9 +856,8 @@ public class HttpRequest extends HttpBase {
* @since 5.4.5
*/
public HttpRequest setHttpProxy(String host, int port) {
- final Proxy proxy = new Proxy(Proxy.Type.HTTP,
- new InetSocketAddress(host, port));
- return setProxy(proxy);
+ config.setHttpProxy(host, port);
+ return this;
}
/**
@@ -896,7 +867,7 @@ public class HttpRequest extends HttpBase {
* @return this
*/
public HttpRequest setProxy(Proxy proxy) {
- this.proxy = proxy;
+ config.setProxy(proxy);
return this;
}
@@ -909,7 +880,7 @@ public class HttpRequest extends HttpBase {
* @return this
*/
public HttpRequest setSSLSocketFactory(SSLSocketFactory ssf) {
- this.ssf = ssf;
+ config.setSSLSocketFactory(ssf);
return this;
}
@@ -930,8 +901,7 @@ public class HttpRequest extends HttpBase {
* @see #setSSLSocketFactory(SSLSocketFactory)
*/
public HttpRequest setSSLProtocol(String protocol) {
- Assert.notBlank(protocol, "protocol must be not blank!");
- setSSLSocketFactory(SSLUtil.createSSLContext(protocol).getSocketFactory());
+ config.setSSLProtocol(protocol);
return this;
}
@@ -957,7 +927,7 @@ public class HttpRequest extends HttpBase {
* @since 4.6.5
*/
public HttpRequest setChunkedStreamingMode(int blockSize) {
- this.blockSize = blockSize;
+ config.setBlockSize(blockSize);
return this;
}
@@ -966,9 +936,31 @@ public class HttpRequest extends HttpBase {
*
* @param interceptor 拦截器实现
* @since 5.7.16
+ * @see #addRequestInterceptor(HttpInterceptor)
*/
- public HttpRequest addInterceptor(HttpInterceptor interceptor) {
- this.interceptors.addChain(interceptor);
+ public HttpRequest addInterceptor(HttpInterceptor interceptor) {
+ return addRequestInterceptor(interceptor);
+ }
+
+ /**
+ * 设置拦截器,用于在请求前重新编辑请求
+ *
+ * @param interceptor 拦截器实现
+ * @since 5.8.0
+ */
+ public HttpRequest addRequestInterceptor(HttpInterceptor interceptor) {
+ config.addRequestInterceptor(interceptor);
+ return this;
+ }
+
+ /**
+ * 设置拦截器,用于在请求前重新编辑请求
+ *
+ * @param interceptor 拦截器实现
+ * @since 5.8.0
+ */
+ public HttpRequest addResponseInterceptor(HttpInterceptor interceptor) {
+ config.addResponseInterceptor(interceptor);
return this;
}
@@ -1002,7 +994,7 @@ public class HttpRequest extends HttpBase {
* @return this
*/
public HttpResponse execute(boolean isAsync) {
- return doExecute(isAsync, this.interceptors);
+ return doExecute(isAsync, config.requestInterceptors, config.responseInterceptors);
}
/**
@@ -1096,12 +1088,14 @@ public class HttpRequest extends HttpBase {
* 执行Reuqest请求
*
* @param isAsync 是否异步
- * @param interceptors 拦截器列表
+ * @param requestInterceptors 请求拦截器列表
+ * @param responseInterceptors 响应拦截器列表
* @return this
*/
- private HttpResponse doExecute(boolean isAsync, HttpInterceptor.Chain interceptors) {
- if (null != interceptors) {
- for (HttpInterceptor interceptor : interceptors) {
+ private HttpResponse doExecute(boolean isAsync, HttpInterceptor.Chain requestInterceptors,
+ HttpInterceptor.Chain responseInterceptors) {
+ if (null != requestInterceptors) {
+ for (HttpInterceptor interceptor : requestInterceptors) {
interceptor.process(this);
}
}
@@ -1118,7 +1112,14 @@ public class HttpRequest extends HttpBase {
// 获取响应
if (null == httpResponse) {
- httpResponse = new HttpResponse(this.httpConnection, this.charset, isAsync, isIgnoreResponseBody());
+ httpResponse = new HttpResponse(this.httpConnection, this.config, this.charset, isAsync, isIgnoreResponseBody());
+ }
+
+ // 拦截响应
+ if (null != responseInterceptors) {
+ for (HttpInterceptor interceptor : responseInterceptors) {
+ interceptor.process(httpResponse);
+ }
}
return httpResponse;
@@ -1134,15 +1135,15 @@ public class HttpRequest extends HttpBase {
}
this.httpConnection = HttpConnection
- .create(this.url.toURL(this.urlHandler), this.proxy)//
- .setConnectTimeout(this.connectionTimeout)//
- .setReadTimeout(this.readTimeout)//
+ .create(this.url.toURL(this.urlHandler), config.proxy)//
+ .setConnectTimeout(config.connectionTimeout)//
+ .setReadTimeout(config.readTimeout)//
.setMethod(this.method)//
- .setHttpsInfo(this.hostnameVerifier, this.ssf)//
+ .setHttpsInfo(config.hostnameVerifier, config.ssf)//
// 关闭JDK自动转发,采用手动转发方式
.setInstanceFollowRedirects(false)
// 流方式上传数据
- .setChunkedStreamingMode(this.blockSize)
+ .setChunkedStreamingMode(config.blockSize)
// 覆盖默认Header
.header(this.headers, true);
@@ -1155,7 +1156,7 @@ public class HttpRequest extends HttpBase {
}
// 是否禁用缓存
- if (this.isDisableCache) {
+ if (config.isDisableCache) {
this.httpConnection.disableCache();
}
}
@@ -1184,7 +1185,7 @@ public class HttpRequest extends HttpBase {
*/
private HttpResponse sendRedirectIfPossible(boolean isAsync) {
// 手动实现重定向
- if (this.maxRedirectCount > 0) {
+ if (config.maxRedirectCount > 0) {
int responseCode;
try {
responseCode = httpConnection.responseCode();
@@ -1197,10 +1198,11 @@ public class HttpRequest extends HttpBase {
if (responseCode != HttpURLConnection.HTTP_OK) {
if (HttpStatus.isRedirected(responseCode)) {
setUrl(UrlBuilder.ofHttpWithoutEncode(httpConnection.header(Header.LOCATION)));
- if (redirectCount < this.maxRedirectCount) {
+ if (redirectCount < config.maxRedirectCount) {
redirectCount++;
// 重定向不再走过滤器
- return doExecute(isAsync, null);
+ return doExecute(isAsync, config.interceptorOnRedirect ? config.requestInterceptors : null,
+ config.interceptorOnRedirect ? config.responseInterceptors : null);
}
}
}
diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java
index d43ae1e6c..9bbeff7c3 100644
--- a/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java
+++ b/hutool-http/src/main/java/cn/hutool/http/HttpResponse.java
@@ -33,6 +33,10 @@ import java.util.Map.Entry;
*/
public class HttpResponse extends HttpBase implements Closeable {
+ /**
+ * Http配置
+ */
+ protected HttpConfig config;
/**
* 持有连接对象
*/
@@ -62,13 +66,15 @@ public class HttpResponse extends HttpBase implements Closeable {
* 构造
*
* @param httpConnection {@link HttpConnection}
+ * @param config Http配置
* @param charset 编码,从请求编码中获取默认编码
* @param isAsync 是否异步
* @param isIgnoreBody 是否忽略读取响应体
* @since 3.1.2
*/
- protected HttpResponse(HttpConnection httpConnection, Charset charset, boolean isAsync, boolean isIgnoreBody) {
+ protected HttpResponse(HttpConnection httpConnection, HttpConfig config, Charset charset, boolean isAsync, boolean isIgnoreBody) {
this.httpConnection = httpConnection;
+ this.config = config;
this.charset = charset;
this.isAsync = isAsync;
this.ignoreBody = isIgnoreBody;
@@ -273,7 +279,7 @@ public class HttpResponse extends HttpBase implements Closeable {
Assert.notNull(out, "[out] must be not null!");
final long contentLength = contentLength();
try {
- return copyBody(bodyStream(), out, contentLength, streamProgress);
+ return copyBody(bodyStream(), out, contentLength, streamProgress, this.config.ignoreEOFError);
} finally {
IoUtil.close(this);
if (isCloseOut) {
@@ -563,7 +569,7 @@ public class HttpResponse extends HttpBase implements Closeable {
final long contentLength = contentLength();
final FastByteArrayOutputStream out = new FastByteArrayOutputStream((int) contentLength);
- copyBody(in, out, contentLength, null);
+ copyBody(in, out, contentLength, null, this.config.ignoreEOFError);
this.bodyBytes = out.toByteArray();
}
@@ -572,13 +578,14 @@ public class HttpResponse extends HttpBase implements Closeable {
* 异步模式下直接读取Http流写出,同步模式下将存储在内存中的响应内容写出
* 写出后会关闭Http流(异步模式)
*
- * @param in 输入流
- * @param out 写出的流
- * @param contentLength 总长度,-1表示未知
- * @param streamProgress 进度显示接口,通过实现此接口显示下载进度
+ * @param in 输入流
+ * @param out 写出的流
+ * @param contentLength 总长度,-1表示未知
+ * @param streamProgress 进度显示接口,通过实现此接口显示下载进度
+ * @param isIgnoreEOFError 是否忽略响应读取时可能的EOF异常
* @return 拷贝长度
*/
- private static long copyBody(InputStream in, OutputStream out, long contentLength, StreamProgress streamProgress) {
+ private static long copyBody(InputStream in, OutputStream out, long contentLength, StreamProgress streamProgress, boolean isIgnoreEOFError) {
if (null == out) {
throw new NullPointerException("[out] is null!");
}
@@ -588,7 +595,7 @@ public class HttpResponse extends HttpBase implements Closeable {
copyLength = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE, contentLength, streamProgress);
} catch (IORuntimeException e) {
//noinspection StatementWithEmptyBody
- if (HttpGlobalConfig.isIgnoreEOFError()
+ if (isIgnoreEOFError
&& (e.getCause() instanceof EOFException || StrUtil.containsIgnoreCase(e.getMessage(), "Premature EOF"))) {
// 忽略读取HTTP流中的EOF错误
} else {
diff --git a/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java b/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
index d60a25f2a..4483e1e28 100644
--- a/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
+++ b/hutool-http/src/test/java/cn/hutool/http/HttpRequestTest.java
@@ -171,13 +171,16 @@ public class HttpRequestTest {
@Test
@Ignore
public void addInterceptorTest() {
- HttpUtil.createGet("https://hutool.cn").addInterceptor(Console::log).execute();
+ HttpUtil.createGet("https://hutool.cn")
+ .addInterceptor(Console::log)
+ .addResponseInterceptor((res)-> Console.log(res.getStatus()))
+ .execute();
}
@Test
@Ignore
public void addGlobalInterceptorTest() {
- GlobalInterceptor.INSTANCE.addInterceptor(Console::log);
+ GlobalInterceptor.INSTANCE.addRequestInterceptor(Console::log);
HttpUtil.createGet("https://hutool.cn").execute();
}