Skip to content

Commit ef768a0

Browse files
committed
fix: Register text/html as default body mapper; resolve mapper by media type
1 parent fd6c003 commit ef768a0

5 files changed

Lines changed: 33 additions & 7 deletions

File tree

src/main/java/com/retailsvc/http/OpenApiServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ private static Map<String, TypeMapper> resolveBodyMappers(
366366
LinkedHashMap<String, TypeMapper> out = new LinkedHashMap<>();
367367
out.put("application/x-www-form-urlencoded", new FormTypeMapper());
368368
out.put("text/plain", new TextTypeMapper());
369+
out.put("text/html", new TextTypeMapper());
369370
out.putAll(userSupplied);
370371
if (!out.containsKey(JSON)) {
371372
TypeMapper fallback = tryLoadGsonMapper();

src/main/java/com/retailsvc/http/TypeMapper.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
/**
44
* Reads and writes request/response bodies for a specific media type. Registered on {@link
55
* OpenApiServer.Builder#bodyMapper(String, TypeMapper)} keyed by media type. The library ships
6-
* built-in mappers for {@code application/x-www-form-urlencoded} and {@code text/plain}; an {@code
7-
* application/json} mapper must be supplied by the caller or auto-detected via Gson on the
8-
* classpath.
6+
* built-in mappers for {@code application/x-www-form-urlencoded}, {@code text/plain}, and {@code
7+
* text/html}; an {@code application/json} mapper must be supplied by the caller or auto-detected
8+
* via Gson on the classpath.
99
*/
1010
public interface TypeMapper {
1111

src/main/java/com/retailsvc/http/internal/ResponseRenderer.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@ private void renderBytes(
7777
}
7878

7979
private byte[] serialize(Object body, String contentType) {
80-
TypeMapper mapper = mappers.get(contentType.toLowerCase(Locale.ROOT));
80+
String mediaType = ContentTypeHeader.mediaType(contentType);
81+
TypeMapper mapper = mappers.get(mediaType.toLowerCase(Locale.ROOT));
8182
if (mapper == null) {
8283
throw new IllegalStateException("No TypeMapper registered for " + contentType);
8384
}

src/main/java/com/retailsvc/http/internal/TextTypeMapper.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44
import java.nio.charset.StandardCharsets;
55

66
/**
7-
* Built-in {@link TypeMapper} for {@code text/plain}. Reads decode bytes using the charset declared
8-
* on {@code Content-Type} (default UTF-8). Writes return {@code String.valueOf(value)} encoded as
9-
* UTF-8.
7+
* Built-in {@link TypeMapper} for textual media types (e.g. {@code text/plain}, {@code text/html}).
8+
* Media-type-agnostic — only the {@code charset} parameter on the {@code Content-Type} header is
9+
* read. Reads decode bytes using that charset (default UTF-8). Writes return {@code
10+
* String.valueOf(value)} encoded as UTF-8.
1011
*/
1112
public final class TextTypeMapper implements TypeMapper {
1213

src/test/java/com/retailsvc/http/ExtraHandlersIT.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,29 @@ void specHandlerServesClasspathResource() throws Exception {
5757
}
5858
}
5959

60+
@Test
61+
void textHtmlResponseIsSerializedByDefaultMapper() throws Exception {
62+
RequestHandler html =
63+
req -> Response.of(200, "<h1>hi</h1>").withContentType("text/html; charset=UTF-8");
64+
65+
try (var s =
66+
newBuilder().spec(spec).handlers(Map.of()).port(0).extraRoute("/page", html).build();
67+
var client = httpClient()) {
68+
69+
var req =
70+
HttpRequest.newBuilder()
71+
.uri(URI.create("http://localhost:" + s.listenPort() + "/page"))
72+
.GET()
73+
.build();
74+
var resp = client.send(req, BodyHandlers.ofString());
75+
76+
assertThat(resp.statusCode()).isEqualTo(200);
77+
assertThat(resp.headers().firstValue("Content-Type"))
78+
.hasValueSatisfying(v -> assertThat(v).startsWith("text/html"));
79+
assertThat(resp.body()).isEqualTo("<h1>hi</h1>");
80+
}
81+
}
82+
6083
@Test
6184
void extraHandlerExceptionFlowsThroughExceptionHandler() throws Exception {
6285
RequestHandler boom =

0 commit comments

Comments
 (0)