Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions avaje-jex/src/main/java/io/avaje/jex/core/JdkContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ final class JdkContext implements Context {
private Map<String, List<String>> queryParams;
private Map<String, String> cookieMap;
private int statusCode;
private byte[] bodyBytes;

private Charset characterEncoding;

Expand All @@ -72,11 +73,7 @@ final class JdkContext implements Context {
}

/** Create when no route matched. */
JdkContext(
ServiceManager mgr,
HttpExchange exchange,
String path,
Set<Role> roles) {
JdkContext(ServiceManager mgr, HttpExchange exchange, String path, Set<Role> roles) {
this.mgr = mgr;
this.roles = roles;
this.exchange = exchange;
Expand Down Expand Up @@ -126,7 +123,10 @@ public String body() {
@Override
public byte[] bodyAsBytes() {
try {
return exchange.getRequestBody().readAllBytes();
if (bodyBytes == null) {
bodyBytes = exchange.getRequestBody().readAllBytes();
}
return bodyBytes;
} catch (IOException e) {
throw new UncheckedIOException(e);
}
Expand All @@ -139,7 +139,12 @@ public InputStream bodyAsInputStream() {

@Override
public <T> T bodyAsType(Type beanType) {
return mgr.fromJson(beanType, bodyAsInputStream());
return mgr.fromJson(beanType, bodyAsBytes());
}

@Override
public <T> T bodyStreamAsType(Type beanType) {
return bodyBytes == null ? mgr.fromJson(beanType, bodyAsInputStream()) : bodyAsType(beanType);
}

private Charset characterEncoding() {
Expand Down
4 changes: 4 additions & 0 deletions avaje-jex/src/main/java/io/avaje/jex/core/ServiceManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ <T> T fromJson(Type type, InputStream is) {
return jsonService.fromJson(type, is);
}

<T> T fromJson(Type type, byte[] data) {
return jsonService.fromJson(type, data);
}

void toJson(Object bean, OutputStream os) {
jsonService.toJson(bean, os);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ public final class JacksonJsonService implements JsonService {

/** Create with defaults for Jackson */
public JacksonJsonService() {
this.mapper =
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
this.mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

/** Create with a Jackson instance that might have custom configuration. */
Expand All @@ -36,16 +35,27 @@ public JacksonJsonService(ObjectMapper mapper) {
@Override
public <T> T fromJson(Type type, InputStream is) {
try {
final var javaType =
javaTypes.computeIfAbsent(
type.getTypeName(), k -> mapper.getTypeFactory().constructType(type));

final var javaType = javaType(type);
return mapper.readValue(is, javaType);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

@Override
public <T> T fromJson(Type type, byte[] data) {
try {
final var javaType = javaType(type);
return mapper.readValue(data, javaType);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}

private JavaType javaType(Type type) {
return javaTypes.computeIfAbsent(type.getTypeName(), k -> mapper.getTypeFactory().constructType(type));
}

@Override
public void toJson(Object bean, OutputStream os) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public <T> T fromJson(Type clazz, InputStream is) {
return jsonb.<T>type(clazz).fromJson(is);
}

@Override
public <T> T fromJson(Type clazz, byte[] data) {
return jsonb.<T>type(clazz).fromJson(data);
}

@Override
public void toJson(Object bean, OutputStream os) {
jsonb.toJson(bean, new NoFlushJsonOutput(os));
Expand Down
17 changes: 17 additions & 0 deletions avaje-jex/src/main/java/io/avaje/jex/http/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ default <T> T bodyAsClass(Class<T> beanType) {
* Returns the request body as an input stream.
*
* @return The request body as an input stream.
* @implNote will return an empty stream if any of the various body methods were called
*/
InputStream bodyAsInputStream();

Expand All @@ -89,6 +90,22 @@ default <T> T bodyAsClass(Class<T> beanType) {
* @param beanType The bean type
*/
<T> T bodyAsType(Type beanType);

/**
* Return the request body as bean using {@link #bodyAsInputStream()}.
*
* @param beanType The bean type
*/
default <T> T bodyStreamAsClass(Class<T> beanType) {
return bodyAsType(beanType);
}

/**
* Return the request body as bean of the given type using {@link #bodyAsInputStream()}.
*
* @param beanType The bean type
*/
<T> T bodyStreamAsType(Type beanType);

/** Return the request content length. */
long contentLength();
Expand Down
14 changes: 10 additions & 4 deletions avaje-jex/src/main/java/io/avaje/jex/spi/JsonService.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,23 @@ public non-sealed interface JsonService extends JexExtension {
String toJsonString(Object bean);

/**
* **Reads JSON from an InputStream**
*
* <p>Reads a JSON-formatted input stream and deserializes it into a Java object of the specified
* type.
* Deserializes a json input stream and deserializes it into a Java object of the specified type.
*
* @param type the Type object of the desired type
* @param is the input stream containing the JSON data
* @return the deserialized object
*/
<T> T fromJson(Type type, InputStream is);

/**
* Deserializes a json byte[] into a Java object of the specified type.
*
* @param type the Type object of the desired type
* @param data the byte[] containing the JSON data
* @return the deserialized object
*/
<T> T fromJson(Type type, byte[] data);

/**
* Serializes a stream of Java objects into a JSON-Stream format, using the {@code x-json-stream}
* media type. Each object in the stream is serialized as a separate JSON object, and the objects
Expand Down
41 changes: 35 additions & 6 deletions avaje-jex/src/test/java/io/avaje/jex/core/ContextTest.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package io.avaje.jex.core;

import io.avaje.jex.Jex;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;

import java.net.http.HttpResponse;
import java.util.Map;
import java.util.Optional;

import static java.util.Objects.requireNonNull;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;

import io.avaje.jex.Jex;

class ContextTest {

Expand Down Expand Up @@ -38,6 +40,15 @@ static TestPair init() {
.post("/echo", ctx -> ctx.text("req-body[" + ctx.body() + "]"))
.get("/{a}/{b}", ctx -> ctx.text("ze-get-" + ctx.pathParamMap()))
.post("/{a}/{b}", ctx -> ctx.text("ze-post-" + ctx.pathParamMap()))
.post("/doubleJsonStream", ctx -> {
ctx.bodyAsInputStream().readAllBytes();
ctx.text(ctx.bodyAsClass(Map.class)+"");
})
.post("/doubleJsonStreamBytes", ctx -> {
ctx.body();
ctx.text(ctx.bodyAsClass(Map.class)+"");
})
.post("/doubleString", ctx -> ctx.text(ctx.body() + ctx.body()))
.get("/status", ctx -> {
ctx.status(201);
ctx.text("status:" + ctx.status());
Expand Down Expand Up @@ -119,11 +130,29 @@ void ctx_methodPathPortProtocol() {
}

@Test
void post_body() {
void post_double_string() {
HttpResponse<String> res = pair.request().path("echo").body("simple").POST().asString();
assertThat(res.body()).isEqualTo("req-body[simple]");
}

@Test
void post_double_json_fail() {
HttpResponse<String> res = pair.request().path("doubleJsonStream").body("{}").POST().asString();
assertThat(res.body()).isEqualTo("Internal Server Error");
}

@Test
void post_double_json_bytes() {
HttpResponse<String> res = pair.request().path("doubleJsonStreamBytes").body("{}").POST().asString();
assertThat(res.body()).isEqualTo("{}");
}

@Test
void post_body() {
HttpResponse<String> res = pair.request().path("doubleString").body("simple").POST().asString();
assertThat(res.body()).isEqualTo("simplesimple");
}

@Test
void get_path_path() {
var res = pair.request()
Expand Down