diff --git a/pom.xml b/pom.xml
index a65f28eb..135311fb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
scx
pom
- 3.1.3
+ 3.1.4
SCX
https://github.com/scx567888/scx
diff --git a/scx-ansi/pom.xml b/scx-ansi/pom.xml
index 2c3b67c2..5c8b8ccd 100644
--- a/scx-ansi/pom.xml
+++ b/scx-ansi/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-ansi
diff --git a/scx-common/pom.xml b/scx-common/pom.xml
index 3159ba34..95903c4b 100644
--- a/scx-common/pom.xml
+++ b/scx-common/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-common
diff --git a/scx-config/pom.xml b/scx-config/pom.xml
index 15fddb84..5ea55043 100644
--- a/scx-config/pom.xml
+++ b/scx-config/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-config
diff --git a/scx-core/pom.xml b/scx-core/pom.xml
index fbf71a2d..b3223337 100644
--- a/scx-core/pom.xml
+++ b/scx-core/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-core
diff --git a/scx-core/src/main/java/cool/scx/core/ScxVersion.java b/scx-core/src/main/java/cool/scx/core/ScxVersion.java
index 2ed15659..26ebb2ce 100644
--- a/scx-core/src/main/java/cool/scx/core/ScxVersion.java
+++ b/scx-core/src/main/java/cool/scx/core/ScxVersion.java
@@ -13,7 +13,7 @@ public class ScxVersion {
/**
* SCX 版本号
*/
- public static final String SCX_VERSION = "3.1.3";
+ public static final String SCX_VERSION = "3.1.4";
/**
* 在控制台上打印 banner
diff --git a/scx-data-jdbc/pom.xml b/scx-data-jdbc/pom.xml
index 46b1498c..baac4f1b 100644
--- a/scx-data-jdbc/pom.xml
+++ b/scx-data-jdbc/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-data-jdbc
diff --git a/scx-data-mysql-x/pom.xml b/scx-data-mysql-x/pom.xml
index 60379049..69bf243e 100644
--- a/scx-data-mysql-x/pom.xml
+++ b/scx-data-mysql-x/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-data-mysql-x
diff --git a/scx-data/pom.xml b/scx-data/pom.xml
index a6c8dab6..700c608a 100644
--- a/scx-data/pom.xml
+++ b/scx-data/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-data
diff --git a/scx-ext/pom.xml b/scx-ext/pom.xml
index 422facce..2a714b95 100644
--- a/scx-ext/pom.xml
+++ b/scx-ext/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-ext
diff --git a/scx-ffm/pom.xml b/scx-ffm/pom.xml
index 7631cfde..5db9ae2c 100644
--- a/scx-ffm/pom.xml
+++ b/scx-ffm/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-ffm
diff --git a/scx-http-helidon/pom.xml b/scx-http-helidon/pom.xml
index 17af6ebe..40550011 100644
--- a/scx-http-helidon/pom.xml
+++ b/scx-http-helidon/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-http-helidon
diff --git a/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHelper.java b/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHelper.java
index 714b7de8..869e2794 100644
--- a/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHelper.java
+++ b/scx-http-helidon/src/main/java/cool/scx/http/helidon/HelidonHelper.java
@@ -3,7 +3,9 @@
import cool.scx.http.ScxHttpHeaders;
import cool.scx.http.ScxHttpHeadersWritable;
import cool.scx.reflect.ReflectFactory;
-import io.helidon.http.*;
+import io.helidon.http.Header;
+import io.helidon.http.Headers;
+import io.helidon.http.PathMatchers;
import io.helidon.webserver.websocket.WsRoute;
import io.helidon.webserver.websocket.WsRouting;
import io.helidon.websocket.WsListener;
@@ -41,11 +43,4 @@ public static ScxHttpHeadersWritable convertHeaders(Headers o) {
return h;
}
- public static void updateHeaders(ScxHttpHeaders o, ServerResponseHeaders u) {
- u.clear();
- for (var header : o) {
- u.add(HeaderNames.create(header.getKey().value()), header.getValue().toArray(String[]::new));
- }
- }
-
}
diff --git a/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/Apple.java b/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/Apple.java
new file mode 100644
index 00000000..695ab5fb
--- /dev/null
+++ b/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/Apple.java
@@ -0,0 +1,5 @@
+package cool.scx.http.helidon.test;
+
+public record Apple(String color, String name, int weight) {
+
+}
diff --git a/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/ClientTest.java b/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/ClientTest.java
index 091b00de..df98e841 100644
--- a/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/ClientTest.java
+++ b/scx-http-helidon/src/test/java/cool/scx/http/helidon/test/ClientTest.java
@@ -1,11 +1,15 @@
package cool.scx.http.helidon.test;
+import cool.scx.http.MediaType;
import cool.scx.http.ScxHttpServerOptions;
import cool.scx.http.helidon.HelidonHttpClient;
import cool.scx.http.helidon.HelidonHttpServer;
+import cool.scx.http.media.form_params.FormParams;
import java.io.IOException;
+import static cool.scx.http.HttpFieldName.ACCEPT;
+
public class ClientTest {
public static void main(String[] args) throws IOException, InterruptedException {
@@ -18,7 +22,8 @@ public static void test1() throws IOException, InterruptedException {
var httpServer = new HelidonHttpServer(new ScxHttpServerOptions().setPort(8990));
httpServer.requestHandler(c -> {
System.out.println(c.uri());
- c.response().send("Hi Client !!!");
+ System.out.println(c.body().asFormParams());
+ c.response().send(new Apple("red", "red apple", 99));
});
httpServer.webSocketHandler(c -> {
System.out.println(c.uri());
@@ -32,7 +37,7 @@ public static void test1() throws IOException, InterruptedException {
public static void test2() {
var httpClient = new HelidonHttpClient();
- var webSocketBuilder = httpClient.webSocket().uri("http://localhost:8990/中:文|路径/ddd?查询=🎈🎈|🎈");
+ var webSocketBuilder = httpClient.webSocket().uri("http://localhost:8990/中:文|路@径/ddd?查询=🎈🎈|🎈#🎃🎃");
webSocketBuilder.onConnect(webSocket -> {
webSocket.onTextMessage(t -> {
System.out.println(t);
@@ -45,9 +50,17 @@ public static void test2() {
public static void test3() {
var httpClient = new HelidonHttpClient();
var response = httpClient.request()
- .uri("http://localhost:8990/中:文|路径/ddd?查询=🎈🎈|🎈")
- .send();
+ //支持 ACCEPT
+ .addHeader(ACCEPT, MediaType.APPLICATION_XML.value())
+ .uri("http://localhost:8990/中:文|路@径/ddd?查询=🎈🎈|🎈#🎃🎃")
+ .send(new FormParams()
+ .add("中文|||/ |||===", "嘎 嘎 嘎🧶🧶🛒")
+ .add("🏓🏓🏓", "!@#%^%&*%%")
+ );
+ //可以用不同的方式重复读取
+ var apple = response.body().asObject(Apple.class);
var string = response.body().asString();
+ var jsonNode = response.body().asJsonNode();
System.out.println(string);
}
diff --git a/scx-http/pom.xml b/scx-http/pom.xml
index 452812a2..6fb124d2 100644
--- a/scx-http/pom.xml
+++ b/scx-http/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-http
diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java b/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java
index e36fa8f4..75e2090e 100644
--- a/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java
+++ b/scx-http/src/main/java/cool/scx/http/ScxHttpBody.java
@@ -1,6 +1,7 @@
package cool.scx.http;
import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.JsonNode;
import cool.scx.http.media.MediaReader;
import cool.scx.http.media.form_params.FormParams;
import cool.scx.http.media.multi_part.MultiPart;
@@ -16,6 +17,7 @@
import static cool.scx.http.media.byte_array.ByteArrayReader.BYTE_ARRAY_READER;
import static cool.scx.http.media.form_params.FormParamsReader.FORM_PARAMS_READER;
+import static cool.scx.http.media.json_node.JsonNodeReader.JSON_NODE_READER;
import static cool.scx.http.media.multi_part.MultiPartStreamCachedReader.MULTI_PART_READER_CACHED;
import static cool.scx.http.media.multi_part.MultiPartStreamReader.MULTI_PART_READER;
import static cool.scx.http.media.string.StringReader.STRING_READER;
@@ -27,7 +29,11 @@ public interface ScxHttpBody {
InputStream inputStream();
- T as(MediaReader t);
+ ScxHttpHeaders headers();
+
+ default T as(MediaReader t) {
+ return t.read(inputStream(), headers());
+ }
default byte[] asBytes() {
return as(BYTE_ARRAY_READER);
@@ -61,6 +67,10 @@ default Path asPath(Path path, OpenOption... options) {
return as(new PathReader(path, options));
}
+ default JsonNode asJsonNode() {
+ return as(JSON_NODE_READER);
+ }
+
default T asObject(Class c) {
return as(new ObjectReader<>(c));
}
diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpBodyImpl.java b/scx-http/src/main/java/cool/scx/http/ScxHttpBodyImpl.java
index 74dd5259..a7074197 100644
--- a/scx-http/src/main/java/cool/scx/http/ScxHttpBodyImpl.java
+++ b/scx-http/src/main/java/cool/scx/http/ScxHttpBodyImpl.java
@@ -1,7 +1,7 @@
package cool.scx.http;
-import cool.scx.http.media.MediaReader;
-
+import java.io.BufferedInputStream;
+import java.io.IOException;
import java.io.InputStream;
public class ScxHttpBodyImpl implements ScxHttpBody {
@@ -9,30 +9,34 @@ public class ScxHttpBodyImpl implements ScxHttpBody {
private final InputStream inputStream;
private final ScxHttpHeaders headers;
- // 简单做一个缓存
- private String cacheString;
+ // InputStream 的缓存
+ private final BufferedInputStream bufferedInputStream;
public ScxHttpBodyImpl(InputStream inputStream, ScxHttpHeaders headers) {
this.inputStream = inputStream;
+ this.bufferedInputStream = new BufferedInputStream(inputStream);
this.headers = headers;
+ this.bufferedInputStream.mark(0);
}
@Override
- public InputStream inputStream() {
- return inputStream;
+ public ScxHttpHeaders headers() {
+ return headers;
}
@Override
- public T as(MediaReader t) {
- return t.read(inputStream, headers);
+ public InputStream inputStream() {
+ try {
+ bufferedInputStream.reset();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return bufferedInputStream;
}
@Override
- public String asString() {
- if (cacheString == null) {
- cacheString = ScxHttpBody.super.asString();
- }
- return cacheString;
+ public String toString() {
+ return asString();
}
}
diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java b/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java
index 53b28cde..16e03f0f 100644
--- a/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java
+++ b/scx-http/src/main/java/cool/scx/http/ScxHttpClientRequest.java
@@ -1,11 +1,15 @@
package cool.scx.http;
+import com.fasterxml.jackson.databind.JsonNode;
import cool.scx.http.content_type.ContentType;
import cool.scx.http.cookie.Cookie;
import cool.scx.http.media.MediaWriter;
import cool.scx.http.media.byte_array.ByteArrayWriter;
import cool.scx.http.media.empty.EmptyWriter;
+import cool.scx.http.media.form_params.FormParams;
+import cool.scx.http.media.form_params.FormParamsWriter;
import cool.scx.http.media.input_stream.InputStreamWriter;
+import cool.scx.http.media.json_node.JsonNodeWriter;
import cool.scx.http.media.multi_part.MultiPart;
import cool.scx.http.media.multi_part.MultiPartWriter;
import cool.scx.http.media.object.ObjectWriter;
@@ -72,10 +76,18 @@ default ScxHttpClientResponse send(InputStream inputStream) {
return send(new InputStreamWriter(inputStream));
}
+ default ScxHttpClientResponse send(FormParams formParams) {
+ return send(new FormParamsWriter(formParams));
+ }
+
default ScxHttpClientResponse send(MultiPart multiPart) {
return send(new MultiPartWriter(multiPart));
}
+ default ScxHttpClientResponse send(JsonNode jsonNode) {
+ return send(new JsonNodeWriter(jsonNode));
+ }
+
default ScxHttpClientResponse send(Object object) {
return send(new ObjectWriter(object));
}
diff --git a/scx-http/src/main/java/cool/scx/http/ScxHttpServerResponse.java b/scx-http/src/main/java/cool/scx/http/ScxHttpServerResponse.java
index 203532ef..3fc13992 100644
--- a/scx-http/src/main/java/cool/scx/http/ScxHttpServerResponse.java
+++ b/scx-http/src/main/java/cool/scx/http/ScxHttpServerResponse.java
@@ -1,11 +1,15 @@
package cool.scx.http;
+import com.fasterxml.jackson.databind.JsonNode;
import cool.scx.http.content_type.ContentType;
import cool.scx.http.cookie.Cookie;
import cool.scx.http.media.MediaWriter;
import cool.scx.http.media.byte_array.ByteArrayWriter;
import cool.scx.http.media.empty.EmptyWriter;
+import cool.scx.http.media.form_params.FormParams;
+import cool.scx.http.media.form_params.FormParamsWriter;
import cool.scx.http.media.input_stream.InputStreamWriter;
+import cool.scx.http.media.json_node.JsonNodeWriter;
import cool.scx.http.media.multi_part.MultiPart;
import cool.scx.http.media.multi_part.MultiPartWriter;
import cool.scx.http.media.object.ObjectWriter;
@@ -77,10 +81,18 @@ default void send(InputStream inputStream) {
send(new InputStreamWriter(inputStream));
}
+ default void send(FormParams formParams) {
+ send(new FormParamsWriter(formParams));
+ }
+
default void send(MultiPart multiPart) {
send(new MultiPartWriter(multiPart));
}
+ default void send(JsonNode jsonNode) {
+ send(new JsonNodeWriter(jsonNode));
+ }
+
default void send(Object object) {
send(new ObjectWriter(object));
}
diff --git a/scx-http/src/main/java/cool/scx/http/media/form_params/FormParams.java b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParams.java
index 30d10ad8..f42ab4ad 100644
--- a/scx-http/src/main/java/cool/scx/http/media/form_params/FormParams.java
+++ b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParams.java
@@ -4,4 +4,24 @@
public class FormParams extends ParametersImpl {
+ @Override
+ public FormParams set(String name, String... value) {
+ return (FormParams) super.set(name, value);
+ }
+
+ @Override
+ public FormParams add(String name, String... value) {
+ return (FormParams) super.add(name, value);
+ }
+
+ @Override
+ public FormParams remove(String name) {
+ return (FormParams) super.remove(name);
+ }
+
+ @Override
+ public FormParams clear() {
+ return (FormParams) super.clear();
+ }
+
}
diff --git a/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsHelper.java b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsHelper.java
new file mode 100644
index 00000000..a0a1cb9f
--- /dev/null
+++ b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsHelper.java
@@ -0,0 +1,39 @@
+package cool.scx.http.media.form_params;
+
+import java.util.ArrayList;
+
+import static java.net.URLDecoder.decode;
+import static java.net.URLEncoder.encode;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class FormParamsHelper {
+
+ public static String encodeFormParams(FormParams formParams) {
+ var l = new ArrayList();
+ for (var formParam : formParams) {
+ var key = formParam.getKey();
+ var values = formParam.getValue();
+ for (var value : values) {
+ var k = encode(key, UTF_8);
+ var v = encode(value, UTF_8);
+ l.add(k + "=" + v);
+ }
+ }
+ return String.join("&", l);
+ }
+
+ public static FormParams decodeFormParams(String str) {
+ var formParams = new FormParams();
+ var pairs = str.split("&");
+ for (var pair : pairs) {
+ var keyValue = pair.split("=", 2);
+ if (keyValue.length == 2) {
+ var key = decode(keyValue[0], UTF_8);
+ var value = decode(keyValue[1], UTF_8);
+ formParams.add(key, value);
+ }
+ }
+ return formParams;
+ }
+
+}
diff --git a/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsReader.java b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsReader.java
index 48a69cf0..8a2fd0a0 100644
--- a/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsReader.java
+++ b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsReader.java
@@ -4,9 +4,8 @@
import cool.scx.http.media.MediaReader;
import java.io.InputStream;
-import java.net.URLDecoder;
-import java.nio.charset.StandardCharsets;
+import static cool.scx.http.media.form_params.FormParamsHelper.decodeFormParams;
import static cool.scx.http.media.string.StringReader.STRING_READER;
public class FormParamsReader implements MediaReader {
@@ -19,18 +18,8 @@ private FormParamsReader() {
@Override
public FormParams read(InputStream inputStream, ScxHttpHeaders headers) {
- var formParams = new FormParams();
var str = STRING_READER.read(inputStream, headers);
- var pairs = str.split("&");
- for (var pair : pairs) {
- var keyValue = pair.split("=");
- if (keyValue.length == 2) {
- String key = URLDecoder.decode(keyValue[0], StandardCharsets.UTF_8);
- String value = URLDecoder.decode(keyValue[1], StandardCharsets.UTF_8);
- formParams.add(key, value);
- }
- }
- return formParams;
+ return decodeFormParams(str);
}
}
diff --git a/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsWriter.java b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsWriter.java
new file mode 100644
index 00000000..22374468
--- /dev/null
+++ b/scx-http/src/main/java/cool/scx/http/media/form_params/FormParamsWriter.java
@@ -0,0 +1,44 @@
+package cool.scx.http.media.form_params;
+
+import cool.scx.http.MediaType;
+import cool.scx.http.ScxHttpHeaders;
+import cool.scx.http.ScxHttpHeadersWritable;
+import cool.scx.http.content_type.ContentType;
+import cool.scx.http.media.MediaWriter;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import static cool.scx.http.media.form_params.FormParamsHelper.encodeFormParams;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class FormParamsWriter implements MediaWriter {
+
+ private final FormParams formParams;
+ private final byte[] bytes;
+
+ public FormParamsWriter(FormParams formParams) {
+ this.formParams = formParams;
+ this.bytes = encodeFormParams(formParams).getBytes(UTF_8);
+ }
+
+ @Override
+ public void beforeWrite(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers) {
+ if (headersWritable.contentLength() == null) {
+ headersWritable.contentLength(bytes.length);
+ }
+ if (headersWritable.contentType() == null) {
+ headersWritable.contentType(ContentType.of(MediaType.APPLICATION_X_WWW_FORM_URLENCODED));
+ }
+ }
+
+ @Override
+ public void write(OutputStream outputStream) {
+ try (outputStream) {
+ outputStream.write(bytes);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/scx-http/src/main/java/cool/scx/http/media/json_node/JsonNodeReader.java b/scx-http/src/main/java/cool/scx/http/media/json_node/JsonNodeReader.java
new file mode 100644
index 00000000..1bd4aef6
--- /dev/null
+++ b/scx-http/src/main/java/cool/scx/http/media/json_node/JsonNodeReader.java
@@ -0,0 +1,53 @@
+package cool.scx.http.media.json_node;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import cool.scx.http.ScxHttpHeaders;
+import cool.scx.http.exception.BadRequestException;
+import cool.scx.http.media.MediaReader;
+
+import java.io.InputStream;
+
+import static cool.scx.common.util.ObjectUtils.jsonMapper;
+import static cool.scx.common.util.ObjectUtils.xmlMapper;
+import static cool.scx.http.MediaType.APPLICATION_JSON;
+import static cool.scx.http.MediaType.APPLICATION_XML;
+import static cool.scx.http.media.string.StringReader.STRING_READER;
+
+public class JsonNodeReader implements MediaReader {
+
+ public static final JsonNodeReader JSON_NODE_READER = new JsonNodeReader();
+
+ @Override
+ public JsonNode read(InputStream inputStream, ScxHttpHeaders requestHeaders) {
+ var str = STRING_READER.read(inputStream, requestHeaders);
+ var contentType = requestHeaders.contentType();
+ var mediaType = contentType != null ? contentType.mediaType() : null;
+ //猜测一下
+ if (APPLICATION_JSON.equals(mediaType)) {
+ try {
+ return jsonMapper().readTree(str);
+ } catch (JsonProcessingException e) {
+ throw new BadRequestException(e);
+ }
+ }
+ if (APPLICATION_XML.equals(mediaType)) {
+ try {
+ return xmlMapper().readTree(str);
+ } catch (JsonProcessingException e) {
+ throw new BadRequestException(e);
+ }
+ }
+ try { //先尝试以 json 格式进行尝试转换
+ return jsonMapper().readTree(str);
+ } catch (Exception exception) {
+ try {//再尝试以 xml 的格式进行转换
+ return xmlMapper().readTree(str);
+ } catch (JsonProcessingException e) {
+ // json 和 xml 均转换失败 直接报错
+ throw new BadRequestException();
+ }
+ }
+ }
+
+}
diff --git a/scx-http/src/main/java/cool/scx/http/media/json_node/JsonNodeWriter.java b/scx-http/src/main/java/cool/scx/http/media/json_node/JsonNodeWriter.java
new file mode 100644
index 00000000..675b9579
--- /dev/null
+++ b/scx-http/src/main/java/cool/scx/http/media/json_node/JsonNodeWriter.java
@@ -0,0 +1,84 @@
+package cool.scx.http.media.json_node;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import cool.scx.http.ScxHttpHeaders;
+import cool.scx.http.ScxHttpHeadersWritable;
+import cool.scx.http.content_type.ContentType;
+import cool.scx.http.content_type.ContentTypeWritable;
+import cool.scx.http.media.MediaWriter;
+
+import java.io.OutputStream;
+
+import static cool.scx.common.util.ObjectUtils.jsonMapper;
+import static cool.scx.common.util.ObjectUtils.xmlMapper;
+import static cool.scx.http.MediaType.APPLICATION_JSON;
+import static cool.scx.http.MediaType.APPLICATION_XML;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class JsonNodeWriter implements MediaWriter {
+
+ private final JsonNode jsonNode;
+ private byte[] data;
+
+ public JsonNodeWriter(JsonNode jsonNode) {
+ this.jsonNode = jsonNode;
+ this.data = null;
+ }
+
+ public static ContentTypeWritable trySetContentType(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers) {
+ //已经设置了则跳过设置
+ if (headersWritable.contentType() != null) {
+ return headersWritable.contentType();
+ }
+ var accepts = headers.accepts();
+ // 如果客户端未指明 accepts 则返回 json
+ if (accepts == null) {
+ headersWritable.contentType(ContentType.of(APPLICATION_JSON).charset(UTF_8));
+ return headersWritable.contentType();
+ }
+ //如果 拥有 XML 或者 JSON 则返回二者其一
+ for (var accept : accepts) {
+ if (accept.mediaType() == APPLICATION_XML) {
+ headersWritable.contentType(ContentType.of(APPLICATION_XML).charset(UTF_8));
+ return headersWritable.contentType();
+ } else if (accept.mediaType() == APPLICATION_JSON) {
+ headersWritable.contentType(ContentType.of(APPLICATION_JSON).charset(UTF_8));
+ return headersWritable.contentType();
+ }
+ }
+ //否则回退到 JSON
+ headersWritable.contentType(ContentType.of(APPLICATION_JSON).charset(UTF_8));
+ return headersWritable.contentType();
+ }
+
+ @Override
+ public void beforeWrite(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers) {
+ var contentType = trySetContentType(headersWritable, headers);
+ //根据类型确定内容长度
+ try {
+ if (contentType.mediaType() == APPLICATION_JSON) {
+ data = jsonMapper().writeValueAsBytes(jsonNode);
+ } else if (contentType.mediaType() == APPLICATION_XML) {
+ data = xmlMapper().writeValueAsBytes(jsonNode);
+ } else {
+ throw new IllegalArgumentException("Unsupported media type: " + contentType.mediaType());
+ }
+ } catch (JsonProcessingException e) {
+ throw new RuntimeException(e);
+ }
+ if (headersWritable.contentLength() == null) {
+ headersWritable.contentLength(data.length);
+ }
+ }
+
+ @Override
+ public void write(OutputStream outputStream) {
+ try (outputStream) {
+ outputStream.write(data);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/scx-http/src/main/java/cool/scx/http/media/object/ObjectReader.java b/scx-http/src/main/java/cool/scx/http/media/object/ObjectReader.java
index 0268dc2a..76599503 100644
--- a/scx-http/src/main/java/cool/scx/http/media/object/ObjectReader.java
+++ b/scx-http/src/main/java/cool/scx/http/media/object/ObjectReader.java
@@ -1,20 +1,14 @@
package cool.scx.http.media.object;
-import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JavaType;
import cool.scx.common.util.ObjectUtils;
import cool.scx.http.ScxHttpHeaders;
-import cool.scx.http.exception.BadRequestException;
import cool.scx.http.media.MediaReader;
import java.io.InputStream;
-import static cool.scx.common.util.ObjectUtils.jsonMapper;
-import static cool.scx.common.util.ObjectUtils.xmlMapper;
-import static cool.scx.http.MediaType.APPLICATION_JSON;
-import static cool.scx.http.MediaType.APPLICATION_XML;
-import static cool.scx.http.media.string.StringReader.STRING_READER;
+import static cool.scx.http.media.json_node.JsonNodeReader.JSON_NODE_READER;
public class ObjectReader implements MediaReader {
@@ -34,44 +28,8 @@ public ObjectReader(JavaType clazz) {
@Override
public T read(InputStream inputStream, ScxHttpHeaders requestHeaders) {
- var str = STRING_READER.read(inputStream, requestHeaders);
- var contentType = requestHeaders.contentType();
- var mediaType = contentType != null ? contentType.mediaType() : null;
- //猜测一下
- return switch (mediaType) {
- case APPLICATION_JSON -> readJson(str);
- case APPLICATION_XML -> readXml(str);
- case null, default -> tryReadOrTextNode(str);
- };
- }
-
- public T readJson(String jsonStr) {
- try {
- return jsonMapper().readValue(jsonStr, type);
- } catch (JsonProcessingException e) {
- throw new BadRequestException(e);
- }
- }
-
- public T readXml(String xmlStr) {
- try {
- return xmlMapper().readValue(xmlStr, type);
- } catch (JsonProcessingException e) {
- throw new BadRequestException(e);
- }
- }
-
- public T tryReadOrTextNode(String str) {
- try { //先尝试以 json 格式进行尝试转换
- return jsonMapper().readValue(str, type);
- } catch (Exception exception) {
- try {//再尝试以 xml 的格式进行转换
- return xmlMapper().readValue(str, type);
- } catch (JsonProcessingException e) {
- // json 和 xml 均转换失败 直接报错
- throw new BadRequestException();
- }
- }
+ var jsonNode = JSON_NODE_READER.read(inputStream, requestHeaders);
+ return ObjectUtils.convertValue(jsonNode, type);
}
}
diff --git a/scx-http/src/main/java/cool/scx/http/media/object/ObjectWriter.java b/scx-http/src/main/java/cool/scx/http/media/object/ObjectWriter.java
index 3d4dfa4f..fbcd20bd 100644
--- a/scx-http/src/main/java/cool/scx/http/media/object/ObjectWriter.java
+++ b/scx-http/src/main/java/cool/scx/http/media/object/ObjectWriter.java
@@ -1,76 +1,34 @@
package cool.scx.http.media.object;
+import com.fasterxml.jackson.databind.JsonNode;
+import cool.scx.common.util.ObjectUtils;
import cool.scx.http.ScxHttpHeaders;
import cool.scx.http.ScxHttpHeadersWritable;
-import cool.scx.http.content_type.ContentType;
-import cool.scx.http.content_type.ContentTypeWritable;
import cool.scx.http.media.MediaWriter;
+import cool.scx.http.media.json_node.JsonNodeWriter;
import java.io.OutputStream;
-import static cool.scx.common.util.ObjectUtils.toJson;
-import static cool.scx.common.util.ObjectUtils.toXml;
-import static cool.scx.http.MediaType.APPLICATION_JSON;
-import static cool.scx.http.MediaType.APPLICATION_XML;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
public class ObjectWriter implements MediaWriter {
private final Object object;
- private byte[] data;
+ private final JsonNode jsonNode;
+ private final JsonNodeWriter jsonNodeWriter;
public ObjectWriter(Object object) {
this.object = object;
- }
-
- public static ContentTypeWritable trySetContentType(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers) {
- var x = ContentType.of(APPLICATION_XML).charset(UTF_8);
- var j = ContentType.of(APPLICATION_JSON).charset(UTF_8);
- //尝试设置 contentType
- if (headersWritable.contentType() == null) {
- var accepts = headers.accepts();
- // 只有明确指定 接受参数是 application/xml 的才返回 xml
- if (accepts != null) {
- for (var accept : accepts) {
- if (accept.mediaType() == APPLICATION_XML) {
- headersWritable.contentType(x);
- return x;
- }
- }
- }
- headersWritable.contentType(j);
- }
- return headersWritable.contentType();
+ this.jsonNode = ObjectUtils.jsonMapper().valueToTree(object);
+ this.jsonNodeWriter = new JsonNodeWriter(this.jsonNode);
}
@Override
public void beforeWrite(ScxHttpHeadersWritable headersWritable, ScxHttpHeaders headers) {
- //尝试设置 contentType
- var ccc = trySetContentType(headersWritable, headers);
- //根据类型确定内容长度
- try {
- if (ccc.mediaType() == APPLICATION_JSON) {
- data = toJson(object).getBytes();
- } else if (ccc.mediaType() == APPLICATION_XML) {
- data = toXml(object).getBytes();
- } else {
- throw new RuntimeException("Unsupported media type: " + ccc.mediaType());
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- if (headersWritable.contentLength() == null) {
- headersWritable.contentLength(data.length);
- }
+ jsonNodeWriter.beforeWrite(headersWritable, headers);
}
@Override
public void write(OutputStream outputStream) {
- try (outputStream) {
- outputStream.write(data);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
+ jsonNodeWriter.write(outputStream);
}
}
diff --git a/scx-http/src/main/java/cool/scx/http/uri/ScxURI.java b/scx-http/src/main/java/cool/scx/http/uri/ScxURI.java
index 5a597d55..9d665c2a 100644
--- a/scx-http/src/main/java/cool/scx/http/uri/ScxURI.java
+++ b/scx-http/src/main/java/cool/scx/http/uri/ScxURI.java
@@ -4,7 +4,7 @@
import java.net.URI;
-import static cool.scx.http.uri.ScxURIHelper.parseURI;
+import static cool.scx.http.uri.URIEncoder.encodeURI;
/**
* ScxURI
@@ -16,7 +16,7 @@ static ScxURIWritable of() {
}
static ScxURIWritable of(String uri) {
- return of(parseURI(uri));
+ return of(URI.create(encodeURI(uri)));
}
static ScxURIWritable of(URI u) {
@@ -55,7 +55,7 @@ default String encode() {
}
default String encode(boolean uriEncoding) {
- return ScxURIHelper.encodeURI(this, uriEncoding);
+ return ScxURIHelper.encodeScxURI(this, uriEncoding);
}
}
diff --git a/scx-http/src/main/java/cool/scx/http/uri/ScxURIHelper.java b/scx-http/src/main/java/cool/scx/http/uri/ScxURIHelper.java
index 29b5feb7..a385fb7e 100644
--- a/scx-http/src/main/java/cool/scx/http/uri/ScxURIHelper.java
+++ b/scx-http/src/main/java/cool/scx/http/uri/ScxURIHelper.java
@@ -3,11 +3,9 @@
import cool.scx.http.Parameters;
import cool.scx.http.ParametersWritable;
-import java.net.URI;
-import java.net.URLEncoder;
import java.util.ArrayList;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import static cool.scx.http.uri.URIEncoder.encodeURIComponent;
/**
* URIHelper
@@ -36,8 +34,8 @@ public static String encodeQuery(Parameters query, boolean uriEn
var value = v.getValue();
for (var s : value) {
if (uriEncoding) {
- var kk = URLEncoder.encode(key, UTF_8);
- var vv = URLEncoder.encode(s, UTF_8);
+ var kk = encodeURIComponent(key);
+ var vv = encodeURIComponent(s);
l.add(kk + "=" + vv);
} else {
l.add(key + "=" + s);
@@ -47,7 +45,7 @@ public static String encodeQuery(Parameters query, boolean uriEn
return String.join("&", l);
}
- public static String encodeURI(ScxURI uri, boolean uriEncoding) {
+ public static String encodeScxURI(ScxURI uri, boolean uriEncoding) {
var scheme = uri.scheme();
var host = uri.host();
var port = uri.port();
@@ -80,7 +78,7 @@ public static String encodeURI(ScxURI uri, boolean uriEncoding) {
//是否需要进行 uri 编码
if (uriEncoding) {
//我们不编码 "/"
- sb.append(URLEncoder.encode(path, UTF_8).replace("%2F", "/"));
+ sb.append(URIEncoder.encodeURI(path));
} else {
sb.append(path);
}
@@ -96,7 +94,7 @@ public static String encodeURI(ScxURI uri, boolean uriEncoding) {
sb.append('#');
//是否需要进行 uri 编码
if (uriEncoding) {
- sb.append(URLEncoder.encode(fragment, UTF_8));
+ sb.append(URIEncoder.encodeURI(fragment));
} else {
sb.append(fragment);
}
@@ -104,23 +102,4 @@ public static String encodeURI(ScxURI uri, boolean uriEncoding) {
return sb.toString();
}
- public static URI parseURI(String uriStr) {
- // 解析 URI
- return URI.create(encodeUri(uriStr));
- }
-
- public static String encodeUri(String uriStr) {
- // 预处理 URI,将特殊字符进行编码
- uriStr = URLEncoder.encode(uriStr, UTF_8)
- .replace("%3A", ":")
- .replace("%2F", "/")
- .replace("%3F", "?")
- .replace("%3D", "=")
- .replace("%26", "&")
- .replace("%23", "#");
-
- // 解析 URI
- return uriStr;
- }
-
}
diff --git a/scx-http/src/main/java/cool/scx/http/uri/URIEncoder.java b/scx-http/src/main/java/cool/scx/http/uri/URIEncoder.java
new file mode 100644
index 00000000..1f99c9fe
--- /dev/null
+++ b/scx-http/src/main/java/cool/scx/http/uri/URIEncoder.java
@@ -0,0 +1,86 @@
+package cool.scx.http.uri;
+
+import java.nio.CharBuffer;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class URIEncoder {
+
+ private static final char[] HEX_DIGITS = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ //按照 codePoint 排列
+ private static final boolean[] DONT_NEED_ENCODING_URI = initDontNeedEncoding(
+ "!", "#", "$", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ ":", ";", "=", "?", "@",
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "_",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ "~"
+ );
+
+ private static final boolean[] DONT_NEED_ENCODING_URI_COMPONENT = initDontNeedEncoding(
+ "!", "'", "(", ")", "*", "-", ".",
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
+ "_",
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ "~"
+ );
+
+ /**
+ * 此方法会生成一个 128 位的 boolean 数组 索引表示 codePoint 值表示是否需要编码
+ *
+ * @param c a
+ * @return a
+ */
+ private static boolean[] initDontNeedEncoding(String... c) {
+ var table = new boolean[128];
+ for (var s : c) {
+ var i = s.codePointAt(0);
+ table[i] = true;
+ }
+ return table;
+ }
+
+ private static String encode(String str, boolean[] dontNeedEncoding) {
+ var sb = new StringBuilder();
+ var codePoints = str.codePoints().toArray();
+ for (int codePoint : codePoints) {
+ //如果 不需要编码
+ if (codePoint < 128 && dontNeedEncoding[codePoint]) {
+ sb.appendCodePoint(codePoint);
+ } else {
+ appendUTF8EncodedCharacter(sb, codePoint);
+ }
+ }
+ return sb.toString();
+ }
+
+ public static String encodeURI(String uri) {
+ return encode(uri, DONT_NEED_ENCODING_URI);
+ }
+
+ public static String encodeURIComponent(String uriComponent) {
+ return encode(uriComponent, DONT_NEED_ENCODING_URI_COMPONENT);
+ }
+
+ private static void appendUTF8EncodedCharacter(StringBuilder sb, int codePoint) {
+ var chars = CharBuffer.wrap(Character.toChars(codePoint));
+ var bytes = UTF_8.encode(chars);
+
+ while (bytes.hasRemaining()) {
+ appendEscape(sb, bytes.get() & 0xFF);
+ }
+ }
+
+ private static void appendEscape(StringBuilder appender, int b) {
+ appender.append('%');
+ appender.append(HEX_DIGITS[b >> 4]);
+ appender.append(HEX_DIGITS[b & 0x0F]);
+ }
+
+}
diff --git a/scx-jdbc-mysql/pom.xml b/scx-jdbc-mysql/pom.xml
index 704b5405..16799188 100644
--- a/scx-jdbc-mysql/pom.xml
+++ b/scx-jdbc-mysql/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-jdbc-mysql
diff --git a/scx-jdbc-sqlite/pom.xml b/scx-jdbc-sqlite/pom.xml
index 9cc485d1..7d2f829e 100644
--- a/scx-jdbc-sqlite/pom.xml
+++ b/scx-jdbc-sqlite/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-jdbc-sqlite
diff --git a/scx-jdbc/pom.xml b/scx-jdbc/pom.xml
index 9efe3865..a302e49f 100644
--- a/scx-jdbc/pom.xml
+++ b/scx-jdbc/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-jdbc
diff --git a/scx-logging/pom.xml b/scx-logging/pom.xml
index dcd6fc99..365c0f96 100644
--- a/scx-logging/pom.xml
+++ b/scx-logging/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-logging
diff --git a/scx-reflect/pom.xml b/scx-reflect/pom.xml
index b12ded1d..d3d5c600 100644
--- a/scx-reflect/pom.xml
+++ b/scx-reflect/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-reflect
diff --git a/scx-scheduling/pom.xml b/scx-scheduling/pom.xml
index 16501287..77e5156f 100644
--- a/scx-scheduling/pom.xml
+++ b/scx-scheduling/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-scheduling
diff --git a/scx-socket/pom.xml b/scx-socket/pom.xml
index 13a0ce62..88a983b4 100644
--- a/scx-socket/pom.xml
+++ b/scx-socket/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-socket
diff --git a/scx-web/pom.xml b/scx-web/pom.xml
index 55b0374e..938834f2 100644
--- a/scx-web/pom.xml
+++ b/scx-web/pom.xml
@@ -6,7 +6,7 @@
cool.scx
scx
- 3.1.3
+ 3.1.4
scx-web