request-validation
Validates the request's headers and/or body against JSON Schemas, and/or a condition expression, rejecting non-conforming requests with a configurable status code. Place it before the upstream node.
Configuration
At least one of header_schema / body_schema / conditions is required.
| Key | Type | Default | Description |
|---|---|---|---|
header_schema | object | — | JSON Schema applied to the request headers, seen as a flat object {name: first_value} with lowercase header names. |
body_schema | object | — | JSON Schema applied to the parsed request body. |
conditions | array | — | A condition expression (rules ANDed at the top level, with nested AND/OR/NOT groups and JSONPath body subjects). Evaluated after the schema checks. |
rejected_code | integer 200–599 | 400 | Response status for rejected requests. |
rejected_msg | string | — | Fixed message returned instead of the validator's error description. |
type: request-validation
config:
rejected_code: 422
body_schema:
type: object
required: [name]
properties:
name: { type: string, minLength: 1 }
header_schema:
type: object
required: [x-api-version]
conditions:
- ["http_authorization", "present"]
- ["$.user.email", "present"]
Schemas are compiled once at config load with the jsonschema crate — malformed schemas (and non-object schema values, missing schemas, out-of-range rejected_code) fail policy compilation, never a live request. conditions is parsed at the same time, with the same fail-fast guarantee.
Behavior
- Headers (when
header_schemais set) — validated as a single-value object: the first value of each header, names lowercased. - Body (when
body_schemais set):- An empty body is rejected.
application/x-www-form-urlencodedbodies are decoded into a flat object mirroringngx.decode_args:a=1&a=2becomes{"a": ["1","2"]}, a bareflag(no=) becomes{"flag": true}, values are percent-decoded.- Any other content type is parsed as JSON; a body that fails to parse is rejected.
- The parsed value is validated against
body_schema.
- JSON normalization — after a successful JSON-body validation the body is re-serialized from the parsed document and the stale
content-lengthheader is removed, so the JSON that was validated is exactly the JSON the upstream receives (guards against JSON interoperability smuggling). Urlencoded bodies are passed through unchanged. - Conditions (when
conditionsis set) — evaluated last, against the (possibly re-serialized) request. A false result rejects the request the same way a schema failure does, with message"request conditions not satisfied"(overridden byrejected_msg, like any other rejection).
On any rejection the plugin writes rejected_code plus the JSON body {"error": "validation_failed", "message": <rejected_msg or validator/condition detail>} onto context.response and exits through the denied port.
The plugin does not write to context.message.
Limitations
- Headers validate the first value of multi-valued headers. Schemas that assert array-typed header values will not match.
- Secret-reference (
$secret://) indirection for schemas is not supported — schemas are literal objects in the node config.
Ports
request-validation declares three output ports: success, denied (a schema-validation or condition rejection is prepared), and error (never actually used — the plugin never fails; malformed schemas and malformed conditions fail at config load, not at request time). Like success, denied is a mandatory port: the policy compiler rejects any policy that leaves it unwired. Wire request-validation.denied straight to client so the prepared rejection reaches the caller instead of continuing into upstream:
edges:
- from: request-validation.success
to: upstream.in
- from: request-validation.denied
to: client.in
Errors
This node never fails at execution time: it always returns through success, so its error port is never taken.