You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -100,13 +78,51 @@ Map<String, Object> raw = new Yaml().load(Files.newInputStream(Path.of("openapi.
100
78
```
101
79
The rest is identical.
102
80
81
+
### JSON mapping
82
+
83
+
The library ships an internal `GsonJsonMapper` that is auto-registered for `application/json` when Gson is on the classpath and no user-supplied JSON mapper has been registered. It:
84
+
85
+
- Returns JSON integers as `Long` and fractional numbers as `Double`.
Register a custom mapper for any media type via `Builder.bodyMapper(mediaType, mapper)`. Built-in defaults:
112
+
113
+
-`application/x-www-form-urlencoded` — read-only. Produces `Map<String, Object>`. A single value is a `String`; repeated keys produce a `List`.
114
+
-`text/plain` — read and write. Produces a decoded `String`; writes via `String.getBytes()`.
115
+
-`application/json` — auto-registered when Gson is on the classpath (see above).
116
+
117
+
User-supplied mappers take precedence over built-in defaults, so you can override any of the above.
118
+
103
119
### Request body content types
104
120
105
-
The server reads `requestBody.content` from the spec and selects a parser by the request's media type (the bare `type/subtype` from `Content-Type`, e.g. `application/json`; lookup is case-insensitive):
121
+
The server reads `requestBody.content` from the spec and selects a mapper by the request's media type (the bare `type/subtype` from `Content-Type`, e.g. `application/json`; lookup is case-insensitive):
|`application/json`|Caller-supplied `JsonMapper`| No — strict against the schema |
125
+
|`application/json`|`GsonJsonMapper` (auto) or caller-supplied `TypeMapper`| No — strict against the schema |
110
126
|`application/x-www-form-urlencoded`| Built-in. `Map<String, Object>`. A single value is a `String`; repeated keys produce a `List`. After coercion the element type tracks the schema (e.g. an `integer` array yields `List<Long>`). | Yes — field values coerced to the property schema type (integer / number / boolean / array of those) |
111
127
|`text/plain`| Built-in. Decoded `String`| No — schema should be `type: string`|
112
128
@@ -159,7 +175,6 @@ to OpenAPI parameter / body validation.
159
175
```java
160
176
var server =OpenApiServer.builder()
161
177
.spec(spec)
162
-
.jsonMapper(mapper)
163
178
.handlers(handlers)
164
179
.addHandler("/alive", Handlers.aliveHandler())
165
180
.addHandler("/schemas/v1/openapi.yaml",
@@ -189,7 +204,6 @@ try-with-resources) via the builder:
189
204
```java
190
205
try (var server =OpenApiServer.builder()
191
206
.spec(spec)
192
-
.jsonMapper(mapper)
193
207
.handlers(handlers)
194
208
.shutdownTimeoutSeconds(5) // close() drains up to 5s; default is 0
195
209
.build()) {
@@ -202,20 +216,15 @@ try (var server = OpenApiServer.builder()
202
216
203
217
## Features
204
218
- OpenAPI specification support
205
-
- Automatic request body parsing for JSON arrays and objects
206
-
- Custom HTTP handler support
207
-
- Built on Java's native `HttpServer` with Thread-Per-Request behaviour using Virtual Threads.
208
-
- Custom integration for JSON serialization/deserialization
219
+
- Automatic request body parsing and response writing per media type via `TypeMapper`
220
+
-`RequestHandler` functional interface — a single `handle(Request)` method replaces raw `HttpExchange` manipulation
221
+
- Fluent `ResponseBuilder` via `request.respond(status)` with terminals: `empty()`, `bytes()`, `text()`, `json()`, `body()`, `stream()`
222
+
- Built-in `GsonJsonMapper` auto-registered when Gson is on the classpath (no explicit wiring needed)
223
+
- Built on Java's native `HttpServer` with Thread-Per-Request behaviour using Virtual Threads
209
224
210
225
211
226
## Handler Registration
212
-
Handlers are registered using string keys that correspond to your OpenAPI operation IDs.
213
-
214
-
215
-
## JSON Mapping
216
-
The library uses a flexible JSON mapping system that automatically detects and parses (using a mapper of choice):
217
-
- JSON arrays (`[...]`)
218
-
- JSON objects (`{...}`)
227
+
Handlers are registered in a `Map<String, RequestHandler>` keyed by OpenAPI `operationId`.
219
228
220
229
## Local development
221
230
@@ -235,7 +244,7 @@ A few things to know:
235
244
236
245
-**Single-process model.** No horizontal scaling primitives are bundled; run multiple instances behind a load balancer for production scale.
237
246
-**JDK HttpServer is the throughput ceiling.** It's documented as a low-throughput / dev-test server. If you need to go materially above the rates above, deploy the same filter/validator/router stack on Jetty, Helidon Níma, or Netty — the spec and validation code is server-agnostic.
238
-
-**Per-request state uses `ScopedValue`** (Java 25, JEP 506), not `HttpExchange.setAttribute`. This matters if a handler offloads work to an executor that's not a `StructuredTaskScope`-managed child thread: the `ScopedValue` is not visible there, so the handler must capture the values it needs (e.g. `byte[] body = Request.bytes();`) before submitting.
239
-
-**`HttpExchange.sendResponseHeaders(rCode, length)` gotcha.** When a handler has no response body, pass `-1` (`Content-Length: 0`, no body); passing`0` produces a chunked response with zero chunks, which is technically non-conformant.
247
+
-**Per-request state uses `ScopedValue`** (Java 25, JEP 506). This matters if a handler offloads work to an executor that's not a `StructuredTaskScope`-managed child thread: the `ScopedValue` is not visible there, so the handler must capture the values it needs (e.g. `byte[] body = request.bytes();`) before submitting.
248
+
-**Empty responses must use `request.respond(status).empty()`**, which sends `responseLength = -1` (`Content-Length: 0`, no body). Passing`0` produces a chunked response with zero chunks, which is technically non-conformant.
0 commit comments