mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix #I85C9S@Gitee
This commit is contained in:
parent
6c39380b6a
commit
74916a4c07
@ -17,11 +17,13 @@ import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.map.MapUtil;
|
||||
import org.dromara.hutool.core.map.multi.ListValueMap;
|
||||
import org.dromara.hutool.core.net.url.UrlBuilder;
|
||||
import org.dromara.hutool.core.net.url.UrlQuery;
|
||||
import org.dromara.hutool.core.text.StrUtil;
|
||||
import org.dromara.hutool.core.util.CharsetUtil;
|
||||
import org.dromara.hutool.core.util.ObjUtil;
|
||||
import org.dromara.hutool.http.GlobalHeaders;
|
||||
import org.dromara.hutool.http.HttpGlobalConfig;
|
||||
import org.dromara.hutool.http.client.body.FormBody;
|
||||
import org.dromara.hutool.http.client.body.HttpBody;
|
||||
import org.dromara.hutool.http.client.body.StringBody;
|
||||
import org.dromara.hutool.http.client.body.UrlEncodedFormBody;
|
||||
@ -105,6 +107,10 @@ public class Request implements HeaderOperation<Request> {
|
||||
* 最大重定向次数
|
||||
*/
|
||||
private int maxRedirectCount;
|
||||
/**
|
||||
* 是否是REST请求模式,REST模式运行GET请求附带body
|
||||
*/
|
||||
private boolean isRest;
|
||||
|
||||
/**
|
||||
* 默认构造
|
||||
@ -148,6 +154,15 @@ public class Request implements HeaderOperation<Request> {
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处理后的请求URL,即如果为非REST的GET请求,将form类型的body拼接为URL的一部分
|
||||
*
|
||||
* @return URL
|
||||
*/
|
||||
public UrlBuilder handledUrl() {
|
||||
return urlWithParamIfGet();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置URL
|
||||
*
|
||||
@ -227,12 +242,24 @@ public class Request implements HeaderOperation<Request> {
|
||||
/**
|
||||
* 获取请求体
|
||||
*
|
||||
* @return this
|
||||
* @return 请求体
|
||||
*/
|
||||
public HttpBody body() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处理过的请求体,即如果是非REST的GET请求,始终返回{@code null}
|
||||
*
|
||||
* @return 请求体
|
||||
*/
|
||||
public HttpBody handledBody() {
|
||||
if (Method.GET.equals(method) && !this.isRest) {
|
||||
return null;
|
||||
}
|
||||
return body();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加请求表单内容
|
||||
*
|
||||
@ -291,6 +318,18 @@ public class Request implements HeaderOperation<Request> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否rest模式<br>
|
||||
* rest模式下get请求不会把参数附加到URL之后,而是作为body发送
|
||||
*
|
||||
* @param isRest 是否rest模式
|
||||
* @return this
|
||||
*/
|
||||
public Request setRest(final boolean isRest) {
|
||||
this.isRest = isRest;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求
|
||||
*
|
||||
@ -306,7 +345,30 @@ public class Request implements HeaderOperation<Request> {
|
||||
* @param engine 自自定义引擎
|
||||
* @return 响应内容
|
||||
*/
|
||||
public Response send(final ClientEngine engine){
|
||||
public Response send(final ClientEngine engine) {
|
||||
return engine.send(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 对于GET请求将参数加到URL中<br>
|
||||
* 此处不对URL中的特殊字符做单独编码<br>
|
||||
* 对于非rest的GET请求,且处于重定向时,参数丢弃
|
||||
*/
|
||||
private UrlBuilder urlWithParamIfGet() {
|
||||
if (Method.GET.equals(method) && !this.isRest) {
|
||||
final HttpBody body = this.body;
|
||||
if (body instanceof FormBody) {
|
||||
final UrlBuilder urlBuilder = UrlBuilder.of(this.url.toURL(), this.url.getCharset());
|
||||
UrlQuery query = urlBuilder.getQuery();
|
||||
if (null == query) {
|
||||
query = UrlQuery.of();
|
||||
urlBuilder.setQuery(query);
|
||||
}
|
||||
query.addAll(((FormBody<?>) body).form());
|
||||
return urlBuilder;
|
||||
}
|
||||
}
|
||||
|
||||
return this.url();
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,15 @@ public abstract class FormBody<T extends FormBody<T>> implements HttpBody {
|
||||
this.charset = charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取表单内容
|
||||
*
|
||||
* @return 表单内容
|
||||
*/
|
||||
public Map<String, Object> form(){
|
||||
return this.form;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置map类型表单数据
|
||||
*
|
||||
|
@ -141,7 +141,7 @@ public class HttpClient4Engine implements ClientEngine {
|
||||
* @return {@link HttpUriRequest}
|
||||
*/
|
||||
private static HttpUriRequest buildRequest(final Request message) {
|
||||
final UrlBuilder url = message.url();
|
||||
final UrlBuilder url = message.handledUrl();
|
||||
Assert.notNull(url, "Request URL must be not null!");
|
||||
final URI uri = url.toURI();
|
||||
|
||||
@ -153,7 +153,7 @@ public class HttpClient4Engine implements ClientEngine {
|
||||
message.headers().forEach((k, v1) -> v1.forEach((v2) -> requestBuilder.addHeader(k, v2)));
|
||||
|
||||
// 填充自定义消息体
|
||||
final HttpBody body = message.body();
|
||||
final HttpBody body = message.handledBody();
|
||||
if(null != body){
|
||||
requestBuilder.setEntity(new HttpClient4BodyEntity(
|
||||
// 用户自定义的内容类型
|
||||
|
@ -31,15 +31,14 @@ import org.apache.hc.core5.http.HttpHost;
|
||||
import org.apache.hc.core5.http.message.BasicHeader;
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.core.net.url.UrlBuilder;
|
||||
import org.dromara.hutool.http.GlobalHeaders;
|
||||
import org.dromara.hutool.http.HttpException;
|
||||
import org.dromara.hutool.http.client.ClientConfig;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||
import org.dromara.hutool.http.client.Request;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.http.client.body.HttpBody;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||
import org.dromara.hutool.http.meta.HeaderName;
|
||||
import org.dromara.hutool.http.proxy.HttpProxy;
|
||||
import org.dromara.hutool.http.ssl.SSLInfo;
|
||||
@ -143,7 +142,7 @@ public class HttpClient5Engine implements ClientEngine {
|
||||
*/
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private static ClassicHttpRequest buildRequest(final Request message) {
|
||||
final UrlBuilder url = message.url();
|
||||
final UrlBuilder url = message.handledUrl();
|
||||
Assert.notNull(url, "Request URL must be not null!");
|
||||
final URI uri = url.toURI();
|
||||
|
||||
@ -153,7 +152,7 @@ public class HttpClient5Engine implements ClientEngine {
|
||||
request.setHeaders(toHeaderList(message.headers()).toArray(new Header[0]));
|
||||
|
||||
// 填充自定义消息体
|
||||
final HttpBody body = message.body();
|
||||
final HttpBody body = message.handledBody();
|
||||
if(null != body){
|
||||
request.setEntity(new HttpClient5BodyEntity(
|
||||
// 用户自定义的内容类型
|
||||
|
@ -100,7 +100,7 @@ public class JdkClientEngine implements ClientEngine {
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
private void doSend(final JdkHttpConnection conn, final Request message) throws IOException {
|
||||
final HttpBody body = message.body();
|
||||
final HttpBody body = message.handledBody();
|
||||
if (null != body) {
|
||||
// 带有消息体,一律按照Rest方式发送
|
||||
body.writeClose(conn.getOutputStream());
|
||||
@ -121,7 +121,7 @@ public class JdkClientEngine implements ClientEngine {
|
||||
final ClientConfig config = ObjUtil.defaultIfNull(this.config, ClientConfig::of);
|
||||
|
||||
final JdkHttpConnection conn = JdkHttpConnection
|
||||
.of(message.url().toURL(), config.getProxy())
|
||||
.of(message.handledUrl().toURL(), config.getProxy())
|
||||
.setConnectTimeout(config.getConnectionTimeout())
|
||||
.setReadTimeout(config.getReadTimeout())
|
||||
.setMethod(message.method())//
|
||||
@ -162,7 +162,7 @@ public class JdkClientEngine implements ClientEngine {
|
||||
|
||||
if (code != HttpURLConnection.HTTP_OK) {
|
||||
if (HttpStatus.isRedirected(code)) {
|
||||
message.url(getLocationUrl(message.url(), conn.header(HeaderName.LOCATION)));
|
||||
message.url(getLocationUrl(message.handledUrl(), conn.header(HeaderName.LOCATION)));
|
||||
if (redirectCount < message.maxRedirectCount()) {
|
||||
redirectCount++;
|
||||
return send(message, isAsync);
|
||||
|
@ -105,9 +105,9 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
|
||||
*/
|
||||
public JdkHttpConnection setMethod(final Method method) {
|
||||
if (Method.POST.equals(method) //
|
||||
|| Method.PUT.equals(method)//
|
||||
|| Method.PATCH.equals(method)//
|
||||
|| Method.DELETE.equals(method)) {
|
||||
|| Method.PUT.equals(method)//
|
||||
|| Method.PATCH.equals(method)//
|
||||
|| Method.DELETE.equals(method)) {
|
||||
this.conn.setUseCaches(false);
|
||||
|
||||
// 增加PATCH方法支持
|
||||
@ -364,7 +364,11 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
|
||||
// 修改为POST,而且无法调用setRequestMethod方法修改,因此此处使用反射强制修改字段属性值
|
||||
// https://stackoverflow.com/questions/978061/http-get-with-request-body/983458
|
||||
if (method == Method.GET && method != getMethod()) {
|
||||
FieldUtil.setFieldValue(this.conn, "method", Method.GET.name());
|
||||
try {
|
||||
FieldUtil.setFieldValue(this.conn, "method", Method.GET.name());
|
||||
} catch (final RuntimeException ignore) {
|
||||
// JDK9+中可能反射失败,忽略之
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
@ -410,7 +414,6 @@ public class JdkHttpConnection implements HeaderOperation<JdkHttpConnection>, Cl
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
|
@ -13,13 +13,12 @@
|
||||
package org.dromara.hutool.http.client.engine.okhttp;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.internal.http.HttpMethod;
|
||||
import org.dromara.hutool.core.io.IORuntimeException;
|
||||
import org.dromara.hutool.http.client.ClientConfig;
|
||||
import org.dromara.hutool.http.client.body.HttpBody;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||
import org.dromara.hutool.http.client.Request;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.http.client.body.HttpBody;
|
||||
import org.dromara.hutool.http.client.engine.ClientEngine;
|
||||
import org.dromara.hutool.http.proxy.HttpProxy;
|
||||
import org.dromara.hutool.http.ssl.SSLInfo;
|
||||
|
||||
@ -126,14 +125,14 @@ public class OkHttpEngine implements ClientEngine {
|
||||
*/
|
||||
private static okhttp3.Request buildRequest(final Request message) {
|
||||
final okhttp3.Request.Builder builder = new okhttp3.Request.Builder()
|
||||
.url(message.url().toURL());
|
||||
.url(message.handledUrl().toURL());
|
||||
|
||||
// 填充方法
|
||||
final String method = message.method().name();
|
||||
final HttpBody body = message.body();
|
||||
final HttpBody body = message.handledBody();
|
||||
// if (HttpMethod.permitsRequestBody(method)) {
|
||||
if (null != body) {
|
||||
// 为了兼容支持rest请求,在此不区分是否为GET等方法,一律按照body是否有值填充
|
||||
// 为了兼容支持rest请求,在此不区分是否为GET等方法,一律按照body是否有值填充,兼容
|
||||
builder.method(method, new OkHttpRequestBody(body));
|
||||
} else {
|
||||
builder.method(method, null);
|
||||
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2023. looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* https://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package org.dromara.hutool.http;
|
||||
|
||||
import org.dromara.hutool.core.lang.Console;
|
||||
import org.dromara.hutool.core.map.MapBuilder;
|
||||
import org.dromara.hutool.http.client.Request;
|
||||
import org.dromara.hutool.http.client.Response;
|
||||
import org.dromara.hutool.http.client.engine.httpclient4.HttpClient4Engine;
|
||||
import org.dromara.hutool.http.meta.Method;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class IssueI85C9STest {
|
||||
@Test
|
||||
void getWithFormTest() {
|
||||
final Response send = Request.of("http://localhost:8888/formTest")
|
||||
.method(Method.GET)
|
||||
.form(MapBuilder.of(new HashMap<String, Object>()).put("a", 1).put("b", 2).build())
|
||||
.send(new HttpClient4Engine());
|
||||
|
||||
Console.log(send.bodyStr());
|
||||
}
|
||||
}
|
@ -27,56 +27,58 @@ public class SimpleServerTest {
|
||||
|
||||
public static void main(final String[] args) {
|
||||
HttpUtil.createServer(8888)
|
||||
.addFilter(((req, res, chain) -> {
|
||||
Console.log("Filter: " + req.getPath());
|
||||
chain.doFilter(req.getHttpExchange());
|
||||
}))
|
||||
// 设置默认根目录,classpath/html
|
||||
.setRoot(FileUtil.file("html"))
|
||||
// get数据测试,返回请求的PATH
|
||||
.addAction("/get", (request, response) ->
|
||||
response.write(request.getURI().toString(), ContentType.TEXT_PLAIN.toString())
|
||||
)
|
||||
// 返回JSON数据测试
|
||||
.addAction("/restTest", (request, response) -> {
|
||||
final String res = JSONUtil.ofObj()
|
||||
.set("id", 1)
|
||||
.set("method", request.getMethod())
|
||||
.set("request", request.getBody())
|
||||
.toStringPretty();
|
||||
response.write(res, ContentType.JSON.toString());
|
||||
})
|
||||
// 获取表单数据测试
|
||||
// http://localhost:8888/formTest?a=1&a=2&b=3
|
||||
.addAction("/formTest", (request, response) ->
|
||||
response.write(request.getParams().toString(), ContentType.TEXT_PLAIN.toString())
|
||||
)
|
||||
.addFilter(((req, res, chain) -> {
|
||||
Console.log("Filter: " + req.getPath());
|
||||
chain.doFilter(req.getHttpExchange());
|
||||
}))
|
||||
// 设置默认根目录,classpath/html
|
||||
.setRoot(FileUtil.file("html"))
|
||||
// get数据测试,返回请求的PATH
|
||||
.addAction("/get", (request, response) ->
|
||||
response.write(request.getURI().toString(), ContentType.TEXT_PLAIN.toString())
|
||||
)
|
||||
// 返回JSON数据测试
|
||||
.addAction("/restTest", (request, response) -> {
|
||||
final String res = JSONUtil.ofObj()
|
||||
.set("id", 1)
|
||||
.set("method", request.getMethod())
|
||||
.set("request", request.getBody())
|
||||
.toStringPretty();
|
||||
response.write(res, ContentType.JSON.toString());
|
||||
})
|
||||
// 获取表单数据测试
|
||||
// http://localhost:8888/formTest?a=1&a=2&b=3
|
||||
.addAction("/formTest", (request, response) -> {
|
||||
Console.log(request.getMethod());
|
||||
response.write(request.getParams().toString(), ContentType.TEXT_PLAIN.toString());
|
||||
}
|
||||
)
|
||||
|
||||
// 文件上传测试
|
||||
// http://localhost:8888/formForUpload.html
|
||||
.addAction("/file", (request, response) -> {
|
||||
Console.log("Upload file...");
|
||||
Console.log(request.getParams());
|
||||
final UploadFile[] files = request.getMultipart().getFiles("file");
|
||||
// 传入目录,默认读取HTTP头中的文件名然后创建文件
|
||||
for (final UploadFile file : files) {
|
||||
file.write("d:/test/");
|
||||
Console.log("Write file: d:/test/" + file.getFileName());
|
||||
}
|
||||
response.write(request.getMultipart().getParamMap().toString(), ContentType.TEXT_PLAIN.toString());
|
||||
}
|
||||
)
|
||||
// 测试输出响应内容是否能正常返回Content-Length头信息
|
||||
.addAction("test/zeroStr", (req, res)-> {
|
||||
res.write("0");
|
||||
Console.log("Write 0 OK");
|
||||
}).addAction("/getCookie", ((request, response) -> {
|
||||
response.setHeader(HeaderName.SET_COOKIE.toString(),
|
||||
ListUtil.of(
|
||||
new HttpCookie("cc", "123").toString(),
|
||||
new HttpCookie("cc", "abc").toString()));
|
||||
response.write("Cookie ok");
|
||||
}))
|
||||
.start();
|
||||
// 文件上传测试
|
||||
// http://localhost:8888/formForUpload.html
|
||||
.addAction("/file", (request, response) -> {
|
||||
Console.log("Upload file...");
|
||||
Console.log(request.getParams());
|
||||
final UploadFile[] files = request.getMultipart().getFiles("file");
|
||||
// 传入目录,默认读取HTTP头中的文件名然后创建文件
|
||||
for (final UploadFile file : files) {
|
||||
file.write("d:/test/");
|
||||
Console.log("Write file: d:/test/" + file.getFileName());
|
||||
}
|
||||
response.write(request.getMultipart().getParamMap().toString(), ContentType.TEXT_PLAIN.toString());
|
||||
}
|
||||
)
|
||||
// 测试输出响应内容是否能正常返回Content-Length头信息
|
||||
.addAction("test/zeroStr", (req, res) -> {
|
||||
res.write("0");
|
||||
Console.log("Write 0 OK");
|
||||
}).addAction("/getCookie", ((request, response) -> {
|
||||
response.setHeader(HeaderName.SET_COOKIE.toString(),
|
||||
ListUtil.of(
|
||||
new HttpCookie("cc", "123").toString(),
|
||||
new HttpCookie("cc", "abc").toString()));
|
||||
response.write("Cookie ok");
|
||||
}))
|
||||
.start();
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user