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
Copy file name to clipboardExpand all lines: README.md
+54Lines changed: 54 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -120,6 +120,59 @@ Register a custom mapper for any media type via `Builder.bodyMapper(mediaType, m
120
120
121
121
User-supplied mappers take precedence over built-in defaults, so you can override any of the above.
122
122
123
+
### Response decorators
124
+
125
+
`Builder.responseDecorator(...)` registers a `ResponseDecorator` that runs whenever a handler calls `request.respond(status)`. Decorators set headers (or other pre-terminal state) before the handler reaches a terminal call. They run in registration order, and handler-supplied headers override decorator-supplied ones (last write wins).
126
+
127
+
Use cases: stamping correlation IDs, tenant IDs, server identifiers, or any cross-cutting header on every response.
`ResponseDecorator` is a `@FunctionalInterface`; the lambda receives the `Request` and the `ResponseBuilder` that's about to be returned from `respond(...)`. Don't call a terminal method (`empty()` / `bytes()` / `json()` / ...) from a decorator — terminals belong to the handler.
142
+
143
+
### Request interceptors
144
+
145
+
`Builder.interceptor(...)` registers a `RequestInterceptor` that wraps every handler invocation. Use it for `ScopedValue` bindings, MDC, authentication, tracing, or any concern that needs to run uniformly around handlers. Interceptors compose in registration order: the first registered runs outermost.
146
+
147
+
```java
148
+
OpenApiServer.builder()
149
+
.spec(spec)
150
+
.handlers(handlers)
151
+
.interceptor(
152
+
(request, next) -> {
153
+
// Resolve once per request.
154
+
String tenant = request.header("X-Tenant-Id");
155
+
ScopedValue.where(TENANT, tenant)
156
+
.call(
157
+
() -> {
158
+
next.proceed();
159
+
returnnull;
160
+
});
161
+
})
162
+
.interceptor(
163
+
(request, next) -> {
164
+
MDC.put("op", request.operationId());
165
+
try {
166
+
next.proceed();
167
+
} finally {
168
+
MDC.remove("op");
169
+
}
170
+
})
171
+
.build();
172
+
```
173
+
174
+
Each interceptor must call `next.proceed()` to continue the chain. Exceptions propagate to the library's standard `ExceptionFilter` and `ExceptionHandler` pipeline.
175
+
123
176
### Request body content types
124
177
125
178
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):
@@ -224,6 +277,7 @@ try (var server = OpenApiServer.builder()
224
277
-`RequestHandler` functional interface — a single `handle(Request)` method replaces raw `HttpExchange` manipulation
225
278
- Fluent `ResponseBuilder` via `request.respond(status)` with terminals: `empty()`, `bytes()`, `text()`, `json()`, `body()`, `stream()`
226
279
- Built-in `GsonJsonMapper` auto-registered when Gson is on the classpath (no explicit wiring needed)
280
+
-`ResponseDecorator` for cross-cutting response headers and `RequestInterceptor` for around-style ScopedValue / MDC / auth concerns
227
281
- Built on Java's native `HttpServer` with Thread-Per-Request behaviour using Virtual Threads
0 commit comments