Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5a4b55e
feat(core): add request validation options with id policies
limehee Mar 4, 2026
c44379b
feat(core): align response validation options and add error code policy
limehee Mar 4, 2026
017d8fd
feat(autoconfigure): bind symmetric validation keys and strict duplic…
limehee Mar 4, 2026
04372ef
docs: update validation key references and migration mapping
limehee Mar 4, 2026
18fd955
feat(core): support strict duplicate detection in raw response parsing
limehee Mar 4, 2026
9f8b2f3
feat(autoconfigure): expose response parser with duplicate-member policy
limehee Mar 4, 2026
e0435c2
docs: clarify response parser bean and duplicate-member behavior
limehee Mar 4, 2026
a117568
fix(validation): complete request duplicate-member option symmetry
limehee Mar 4, 2026
d5d5223
test: expand validation and parser coverage for core/autoconfigure
limehee Mar 5, 2026
3e1fe6b
feat(samples): add strict validation profile examples for pure and sp…
limehee Mar 5, 2026
cf8f05e
fix(core,webmvc): enforce reserved method rule and fail-fast construc…
limehee Mar 5, 2026
1d675a5
docs(metadata): sync guides and IDE hints with validation and fail-fa…
limehee Mar 5, 2026
7a84ab2
fix(core): align reject-field validation when source is unavailable
limehee Mar 5, 2026
a9c3f8e
refactor(parsing): unify duplicate-member handling via shared payload…
limehee Mar 5, 2026
c18f490
refactor(core): align incoming response model conventions
limehee Mar 5, 2026
25a4661
refactor(validation): normalize request/response error message conven…
limehee Mar 5, 2026
fbfe1a4
test(autoconfigure): verify independent request/response boolean bind…
limehee Mar 5, 2026
387cd80
build(sample): remove hardcoded core version in pure-java demo
limehee Mar 5, 2026
66514e9
docs(samples): add explicit test execution steps
limehee Mar 5, 2026
b4c9710
docs(index): add direct links to sample guides
limehee Mar 5, 2026
2449ed2
docs(config): clarify legacy response validation keys are migration-only
limehee Mar 5, 2026
26b883b
docs(sample): add expected JSON responses for spring demo scenarios
limehee Mar 5, 2026
f524c4d
test: add missing branch coverage for metrics and registrar paths
limehee Mar 5, 2026
12f399c
fix(autoconfigure): honor custom validation options for duplicate-mem…
limehee Mar 5, 2026
e7afe7d
fix(metrics): enforce positive method tag cardinality limit
limehee Mar 5, 2026
05a2104
fix(metrics): enforce hard cap for method tag cardinality
limehee Mar 5, 2026
4d77ef6
docs(guide): improve formatting and examples in Spring Boot guide
limehee Mar 5, 2026
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
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,16 @@ Use `jsonrpc.validation.request.*` and `jsonrpc.validation.response.*` for fine-
jsonrpc:
validation:
request:
require-id-member: false
allow-fractional-id: false
reject-response-fields: true
params-type-violation-code-policy: INVALID_REQUEST
response:
require-response-id-member: true
allow-fractional-response-id: false
allow-request-fields-in-response: false
require-id-member: true
allow-fractional-id: false
reject-request-fields: true
error-code:
policy: STANDARD_OR_SERVER_ERROR_RANGE
```

For the full list of validation keys and defaults, see
Expand Down
115 changes: 90 additions & 25 deletions docs/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,30 @@ All properties are under `jsonrpc.*` and are bound to `JsonRpcProperties`.
| `jsonrpc.max-request-bytes` | `int` | `1048576` | Raw HTTP request payload size limit in bytes |
| `jsonrpc.scan-annotated-methods` | `boolean` | `true` | Scan Spring beans for `@JsonRpcMethod` |
| `jsonrpc.include-error-data` | `boolean` | `false` | Include `JsonRpcException.data` in error responses |
| `jsonrpc.validation.request.require-json-rpc-version-20` | `boolean` | `true` | Require incoming request `jsonrpc` to equal `"2.0"` |
| `jsonrpc.validation.request.require-id-member` | `boolean` | `false` | Require incoming requests to include an `id` member |
| `jsonrpc.validation.request.allow-null-id` | `boolean` | `true` | Allow `id: null` in incoming requests |
| `jsonrpc.validation.request.allow-string-id` | `boolean` | `true` | Allow string IDs in incoming requests |
| `jsonrpc.validation.request.allow-numeric-id` | `boolean` | `true` | Allow numeric IDs in incoming requests |
| `jsonrpc.validation.request.allow-fractional-id` | `boolean` | `true` | Allow fractional numeric IDs in incoming requests |
| `jsonrpc.validation.request.reject-response-fields` | `boolean` | `false` | Reject request objects containing `result`/`error` |
| `jsonrpc.validation.request.reject-duplicate-members` | `boolean` | `false` | Reject duplicate members while parsing raw request JSON |
| `jsonrpc.validation.request.params-type-violation-code-policy` | `INVALID_PARAMS` or `INVALID_REQUEST` | `INVALID_PARAMS` | Error code used when `params` exists but is neither object nor array |
| `jsonrpc.validation.response.require-json-rpc-version-20` | `boolean` | `true` | Require incoming response `jsonrpc` to equal `"2.0"` |
| `jsonrpc.validation.response.require-response-id-member` | `boolean` | `true` | Require incoming responses to include an `id` member |
| `jsonrpc.validation.response.allow-null-response-id` | `boolean` | `true` | Allow `id: null` in incoming responses |
| `jsonrpc.validation.response.allow-string-response-id` | `boolean` | `true` | Allow string IDs in incoming responses |
| `jsonrpc.validation.response.allow-numeric-response-id` | `boolean` | `true` | Allow numeric IDs in incoming responses |
| `jsonrpc.validation.response.allow-fractional-response-id` | `boolean` | `true` | Allow fractional numeric IDs in incoming responses |
| `jsonrpc.validation.response.require-id-member` | `boolean` | `true` | Require incoming responses to include an `id` member |
| `jsonrpc.validation.response.allow-null-id` | `boolean` | `true` | Allow `id: null` in incoming responses |
| `jsonrpc.validation.response.allow-string-id` | `boolean` | `true` | Allow string IDs in incoming responses |
| `jsonrpc.validation.response.allow-numeric-id` | `boolean` | `true` | Allow numeric IDs in incoming responses |
| `jsonrpc.validation.response.allow-fractional-id` | `boolean` | `true` | Allow fractional numeric IDs in incoming responses |
| `jsonrpc.validation.response.require-exclusive-result-or-error` | `boolean` | `true` | Require exactly one of `result` or `error` |
| `jsonrpc.validation.response.require-error-object-when-present` | `boolean` | `true` | Require `error` to be an object when present |
| `jsonrpc.validation.response.require-integer-error-code` | `boolean` | `true` | Require `error.code` to be an integer |
| `jsonrpc.validation.response.require-string-error-message` | `boolean` | `true` | Require `error.message` to be a string |
| `jsonrpc.validation.response.allow-request-fields-in-response` | `boolean` | `true` | Allow request-only fields (`method`/`params`) on responses |
| `jsonrpc.validation.response.reject-request-fields` | `boolean` | `false` | Reject response objects containing `method`/`params` |
| `jsonrpc.validation.response.reject-duplicate-members` | `boolean` | `false` | Reject duplicate members while parsing raw response JSON |
| `jsonrpc.validation.response.error-code.policy` | `JsonRpcResponseErrorCodePolicy` | `ANY_INTEGER` | Accepted integer range policy for response `error.code` |
| `jsonrpc.validation.response.error-code.range.min` | `Integer` | `null` | Inclusive minimum for `CUSTOM_RANGE` |
| `jsonrpc.validation.response.error-code.range.max` | `Integer` | `null` | Inclusive maximum for `CUSTOM_RANGE` |
| `jsonrpc.method-registration-conflict-policy` | `REJECT` or `REPLACE` | `REJECT` | Duplicate method name registration policy |
| `jsonrpc.method-allowlist` | `List<String>` | `[]` | Allowlist for method access filtering |
| `jsonrpc.method-denylist` | `List<String>` | `[]` | Denylist for method access filtering (higher priority) |
Expand All @@ -34,6 +46,12 @@ All properties are under `jsonrpc.*` and are bound to `JsonRpcProperties`.
| `jsonrpc.notification-executor-enabled` | `boolean` | `false` | Enable executor-backed notification dispatch |
| `jsonrpc.notification-executor-bean-name` | `String` | `""` | Preferred executor bean name for notifications |

`JsonRpcResponseErrorCodePolicy` values:
- `ANY_INTEGER`
- `STANDARD_ONLY`
- `STANDARD_OR_SERVER_ERROR_RANGE`
- `CUSTOM_RANGE`

## 2. Validation Rules (Fail Fast)

Startup fails with `IllegalArgumentException` when any of these conditions occur:
Expand All @@ -52,12 +70,18 @@ Startup fails with `IllegalArgumentException` when any of these conditions occur
- `jsonrpc.validation.request` is null
- `jsonrpc.validation.request.params-type-violation-code-policy` is null
- `jsonrpc.validation.response` is null
- `jsonrpc.validation.response.error-code` is null
- `jsonrpc.validation.response.error-code.policy` is null
- `jsonrpc.validation.response.error-code.range` is null
- `jsonrpc.validation.response.require-integer-error-code=false` with `jsonrpc.validation.response.error-code.policy != ANY_INTEGER`
- `jsonrpc.validation.response.error-code.policy=CUSTOM_RANGE` and either range bound is missing
- `jsonrpc.validation.response.error-code.policy=CUSTOM_RANGE` and `range.min > range.max`
- allowlist/denylist list itself is null
- allowlist/denylist contains null or blank values

## 3. Runtime Behavior Priority

## 3.1 Method access filtering
### 3.1 Method access filtering

Priority:

Expand All @@ -69,9 +93,10 @@ Rules:

- denylist always overrides allowlist
- if allowlist is non-empty, methods not in allowlist are denied
- `rpc.*` methods are blocked by registry independently of allow/deny lists
- `rpc.*` methods are blocked by request validation, and also by the default registry independently of allow/deny
lists

## 3.2 Notification executor resolution
### 3.2 Notification executor resolution

When `jsonrpc.notification-executor-enabled=true`, resolution order is:

Expand All @@ -82,7 +107,7 @@ When `jsonrpc.notification-executor-enabled=true`, resolution order is:

If a configured bean name is missing, startup fails.

## 3.3 Method registration conflict handling
### 3.3 Method registration conflict handling

- `REJECT`: first duplicate fails registration.
- `REPLACE`: later registration wins.
Expand All @@ -108,7 +133,7 @@ Example environment variable mapping:

## 5. Example Configurations

## 5.1 Baseline production profile
### 5.1 Baseline production profile

```yaml
jsonrpc:
Expand All @@ -120,40 +145,57 @@ jsonrpc:
include-error-data: false
validation:
request:
require-json-rpc-version-20: true
require-id-member: false
allow-null-id: true
allow-string-id: true
allow-numeric-id: true
allow-fractional-id: true
reject-response-fields: false
reject-duplicate-members: false
params-type-violation-code-policy: INVALID_PARAMS
response:
require-json-rpc-version-20: true
require-response-id-member: true
allow-null-response-id: true
allow-string-response-id: true
allow-numeric-response-id: true
allow-fractional-response-id: true
require-id-member: true
allow-null-id: true
allow-string-id: true
allow-numeric-id: true
allow-fractional-id: true
require-exclusive-result-or-error: true
require-error-object-when-present: true
require-integer-error-code: true
require-string-error-message: true
allow-request-fields-in-response: true
reject-request-fields: false
reject-duplicate-members: false
error-code:
policy: ANY_INTEGER
range:
min: null
max: null
method-allowlist: [ ]
method-denylist: [ ]
```

## 5.2 Strict access profile
### 5.2 Strict response error-code profile

```yaml
jsonrpc:
method-allowlist: [ user.find, user.update ]
method-denylist: [ user.delete ]
validation:
response:
reject-request-fields: true
error-code:
policy: STANDARD_OR_SERVER_ERROR_RANGE
```

## 5.3 Async notification profile
### 5.3 Async notification profile

```yaml
jsonrpc:
notification-executor-enabled: true
notification-executor-bean-name: applicationTaskExecutor
```

## 5.4 Metrics-rich profile
### 5.4 Metrics-rich profile

```yaml
jsonrpc:
Expand All @@ -163,7 +205,26 @@ jsonrpc:
metrics-max-method-tag-values: 200
```

## 6. IDE Auto-completion and Metadata
## 6. Migration Notes (Response Validation Key Rename)

The old keys below are migration references only.
They are not bound by current auto-configuration and should not be used in new setups.
Use the canonical symmetric keys in the right column.

| Old key | New key |
|----------------------------------------------------------------|-----------------------------------------------------|
| `jsonrpc.validation.response.require-response-id-member` | `jsonrpc.validation.response.require-id-member` |
| `jsonrpc.validation.response.allow-null-response-id` | `jsonrpc.validation.response.allow-null-id` |
| `jsonrpc.validation.response.allow-string-response-id` | `jsonrpc.validation.response.allow-string-id` |
| `jsonrpc.validation.response.allow-numeric-response-id` | `jsonrpc.validation.response.allow-numeric-id` |
| `jsonrpc.validation.response.allow-fractional-response-id` | `jsonrpc.validation.response.allow-fractional-id` |
| `jsonrpc.validation.response.allow-request-fields-in-response` | `jsonrpc.validation.response.reject-request-fields` |

Inversion rule:

- `reject-request-fields = !allow-request-fields-in-response`

## 7. IDE Auto-completion and Metadata

The project ships Spring Boot configuration metadata via:

Expand All @@ -173,10 +234,14 @@ The project ships Spring Boot configuration metadata via:
This enables:

- property key completion
- enum value suggestions (`REJECT`, `REPLACE`, `INVALID_PARAMS`, `INVALID_REQUEST`)
- enum value suggestions (`REJECT`, `REPLACE`, `INVALID_PARAMS`, `INVALID_REQUEST`, error-code policy values)
- example numeric suggestions for `jsonrpc.validation.response.error-code.range.min/max`
- metadata hints in IntelliJ and Spring-aware tooling

## 7. Related References
Hints are suggestions for IDE completion only. They do not restrict allowed runtime values unless validation rules
explicitly enforce constraints.

## 8. Related References

- Spring setup details: [`spring-boot-guide.md`](spring-boot-guide.md)
- Binding and registration details: [`registration-and-binding.md`](registration-and-binding.md)
Expand Down
7 changes: 6 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,12 @@
- Failure diagnosis and fixes: [`troubleshooting.md`](troubleshooting.md)
- Release and publish flow: [`release-checklist.md`](release-checklist.md)

## 5. Repository-Level Docs
## 5. Samples

- Spring Boot sample: [`../samples/spring-boot-demo/README.md`](../samples/spring-boot-demo/README.md)
- Pure Java sample: [`../samples/pure-java-demo/README.md`](../samples/pure-java-demo/README.md)

## 6. Repository-Level Docs

- Project overview: [`../README.md`](../README.md)
- Contribution workflow: [`../CONTRIBUTING.md`](../CONTRIBUTING.md)
Expand Down
25 changes: 16 additions & 9 deletions docs/protocol-and-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ Implementation constants are in `JsonRpcErrorCode` and messages in `JsonRpcConst
- `JsonRpcResponseValidator`
- `JsonRpcResponseValidationOptions`

`DefaultJsonRpcResponseParser` can parse from `JsonNode`, `String`, or `byte[]`, and can optionally reject duplicate
members during raw JSON parsing.

These APIs are transport-agnostic and useful for bidirectional channels (for example WebSocket) where
request/response envelopes may arrive on the same connection.

Expand All @@ -79,18 +82,21 @@ This library does not expose predefined strict/lenient modes; policy is controll
`JsonRpcResponseValidationOptions` exposes per-rule switches:

- `requireJsonRpcVersion20` (default: `true`)
- `requireResponseIdMember` (default: `true`)
- `allowNullResponseId` (default: `true`)
- `allowStringResponseId` (default: `true`)
- `allowNumericResponseId` (default: `true`)
- `allowFractionalResponseId` (default: `true`)
- `requireIdMember` (default: `true`)
- `allowNullId` (default: `true`)
- `allowStringId` (default: `true`)
- `allowNumericId` (default: `true`)
- `allowFractionalId` (default: `true`)
- `requireExclusiveResultOrError` (default: `true`)
- `requireErrorObjectWhenPresent` (default: `true`)
- `requireIntegerErrorCode` (default: `true`)
- `requireStringErrorMessage` (default: `true`)
- `allowRequestFieldsInResponse` (default: `true`)
- `rejectRequestFields` (default: `false`)
- `rejectDuplicateMembers` (default: `false`)
- `errorCodePolicy` (default: `ANY_INTEGER`)
- `errorCodeRangeMin` / `errorCodeRangeMax` (default: `null`, used with `CUSTOM_RANGE`)

`allowRequestFieldsInResponse=true` is a compatibility default and is not an RFC MUST rule.
`rejectRequestFields=false` is a compatibility default and is not an RFC MUST rule.

## `id` Handling Details

Expand All @@ -108,8 +114,9 @@ This library does not expose predefined strict/lenient modes; policy is controll

## Reserved Method Namespace

Methods starting with `rpc.` are rejected at registration (`IllegalArgumentException`) to preserve reserved namespace
semantics.
Methods starting with `rpc.` are treated as invalid requests during request validation (`-32600`).
Default in-memory registration also rejects `rpc.*` method names (`IllegalArgumentException`), so both registration and
dispatch paths preserve reserved namespace semantics.

## HTTP Mapping Notes

Expand Down
1 change: 1 addition & 0 deletions docs/pure-java-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ JsonRpcDispatcher dispatcher = new JsonRpcDispatcher(
```

This keeps protocol behavior while letting you customize policy and implementation.
`maxBatchSize` must be greater than `0` (fail-fast `IllegalArgumentException` otherwise).

`JsonRpcParamsTypeViolationCodePolicy` controls which code is used when request `params` is present but not
an object/array:
Expand Down
Loading