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
54 changes: 42 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
A lightweight Java library that wraps the JDK's `com.sun.net.httpserver.HttpServer` and serves
endpoints declared in an OpenAPI 3.1.x specification. Handlers are pure functions registered by
`operationId`; the framework handles routing, OpenAPI parameter and body validation, JSON
(de)serialisation, and RFC 7807 error rendering.
(de)serialisation, and RFC 9457 error rendering.

## Table of contents

Expand All @@ -27,7 +27,7 @@ endpoints declared in an OpenAPI 3.1.x specification. Handlers are pure function
- [After-response hooks](#after-response-hooks)
- [Security](#security)
- [Request body content types](#request-body-content-types)
- [Error responses (RFC 7807)](#error-responses-rfc-7807)
- [Error responses (RFC 9457)](#error-responses-rfc-9457)
- [Extra (non-OpenAPI) handlers](#extra-non-openapi-handlers)
- [Health endpoint](#health-endpoint)
- [Graceful shutdown](#graceful-shutdown)
Expand All @@ -47,7 +47,7 @@ endpoints declared in an OpenAPI 3.1.x specification. Handlers are pure function
`ResponseDecorator` for cross-cutting response headers
- OpenAPI `securitySchemes` and `security` enforcement (`apiKey`, `http bearer`, `http basic`),
with an opt-out for sidecar / gateway authentication
- RFC 7807 `application/problem+json` validation errors with JSON-Pointer to the failing location
- RFC 9457 `application/problem+json` validation errors with an `errors[]` array of JSON-Pointers to the failing locations
- Built on the JDK's native `HttpServer` with thread-per-request behaviour using virtual threads

## Maven artifact
Expand Down Expand Up @@ -630,7 +630,7 @@ to detect errors.

The library parses `components.securitySchemes` and the `security` requirement lists (root-level
and per-operation), extracts the credential per scheme, hands it to a consumer-provided
`SchemeValidator` callback, and renders RFC 7807 `application/problem+json` rejections — 401 for
`SchemeValidator` callback, and renders RFC 9457 `application/problem+json` rejections — 401 for
missing/malformed credentials (with `WWW-Authenticate`), 403 when the validator denies.

Supported scheme types in this release:
Expand Down Expand Up @@ -870,28 +870,38 @@ case-insensitive):

Form-field coercion mirrors the rules already used at the parameter boundary: the wire is
string-only by definition, so a property typed as `integer` accepts `"42"` and yields `42`.
Coercion failures surface as RFC-7807 `400` responses with a JSON-pointer to the failing field.
Coercion failures surface as RFC-9457 `400` responses with a JSON-pointer to the failing field.

Both built-in parsers honour the `charset=` parameter on the `Content-Type` header (default
UTF-8). Unknown charsets fall back to UTF-8.

## Error responses (RFC 7807)
## Error responses (RFC 9457)

Validation failures — missing required fields, type mismatches, unsupported content types,
coercion errors, malformed bodies — produce an `HTTP 400 Bad Request` response with body media
type `application/problem+json`, following
[RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807).
[RFC 9457](https://datatracker.ietf.org/doc/html/rfc9457) (which obsoletes RFC 7807).

A single error is reported per request (first failure wins). The response body has these fields:
The top level carries the RFC core members; each individual failure is an entry in an `errors`
array (an RFC 9457 extension member). A non-combinator failure yields a single entry; a
`oneOf` / `anyOf` failure yields one entry per failed branch, ordered most-likely-cause first
(the branch the payload most resembles) and de-duplicated.

| Field | Type | Description |
| ---------- | ------- | ---------------------------------------------------------------------------------------- |
| `type` | string | Always `about:blank` (no per-error type URI). |
| `title` | string | Always `Bad Request`. |
| `status` | integer | Always `400`. |
| `detail` | string | Human-readable description of the failure (e.g. `expected integer`). |
| `pointer` | string | [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) JSON-Pointer to the failing location (e.g. `/body/age`, `/query/limit`, `/path/id`, or `/body` for body-wide errors). |
| `detail` | string | Human-readable description (a leaf message; for a combinator, `matched 0 of N oneOf branches` or `did not match any anyOf branch`). |
| `errors` | array | One entry per failure; omitted when empty. Each entry has the fields below. |

Each `errors[]` entry:

| Field | Type | Description |
| ---------- | ------- | ---------------------------------------------------------------------------------------- |
| `pointer` | string | [RFC 6901](https://datatracker.ietf.org/doc/html/rfc6901) JSON-Pointer to the failing location, as a URI fragment — e.g. `#/age` for a body field, `#/query/limit` / `#/path/id` for parameters, `#/body` for whole-body errors (missing body, unsupported content type), or `#` when the entire body is the wrong type. |
| `keyword` | string | The validation rule that failed: `type`, `required`, `enum`, `pattern`, `format`, `minimum`, `maximum`, `minLength`, `maxLength`, `additionalProperties`, `oneOf`, `anyOf`, `allOf`, `not`, `const`, `content-type`, `decode`, … |
| `detail` | string | Human-readable description of this failure (e.g. `expected integer`). |

Example body for `POST /form-echo` with `age=abc` (`age` is declared as `integer`):

Expand All @@ -901,11 +911,31 @@ Example body for `POST /form-echo` with `age=abc` (`age` is declared as `integer
"title": "Bad Request",
"status": 400,
"detail": "expected integer",
"pointer": "/age",
"keyword": "type"
"errors": [
{ "pointer": "#/age", "keyword": "type", "detail": "expected integer" }
]
}
```

Example body for a `oneOf` request body that matches no branch — one entry per failed branch,
deepest (most-likely) first:

``` json
{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "matched 0 of 2 oneOf branches",
"errors": [
{ "pointer": "#/pet/collar/size", "keyword": "type", "detail": "expected integer" },
{ "pointer": "#/pet/bark", "keyword": "type", "detail": "expected boolean" }
]
}
```

When several branches fail at the same location for the same reason, those identical entries are
collapsed into one — so a `oneOf` failure can show fewer entries than it has branches.

Other error responses:

- **404 Not Found** — no route matches the request path (no body).
Expand Down
Loading
Loading