Query-param smuggle-rejection
Read endpoints should reject any query parameter they do not explicitly bind.
Otherwise a client could "smuggle" a parameter such as ?tenantId= in an attempt to override server-resolved context.
QueryParamGuard blocks this, and OrderFieldsGuard (or the static OrderFields) does the same for the orderByField value.
QueryParamGuard
The guard derives its allow-list once, from the @QueryValue properties of the introspected query beans you pass it.
There is no hand-maintained string list to drift: adding a field to a query bean automatically widens the allow-list.
private final QueryParamGuard queryParamGuard = QueryParamGuard.fromBeans(Paging.class, WidgetQuery.class);
Capture every incoming parameter with a catch-all binding and hand the map to the guard:
@Get("{?queryParams*}")
public SliceEnvelope<Widget> list(
@RequestBean Paging paging,
@RequestBean WidgetQuery query,
@Nullable @QueryValue Map<String, String> queryParams
) {
queryParamGuard.rejectUnknown(queryParams);
// ...
}
Any parameter whose head segment (before a .) is not in the allow-list is rejected with a HttpProblemCode.UnsupportedQueryParameter 400.
The problem carries the offending parameter name and the allowed set.
Order-field validation
The orderByField value is a parameter value, not a parameter name, so QueryParamGuard does not validate it.
Restrict it to a known set of sortable columns.
The instance style mirrors QueryParamGuard — build the guard once with the allow-list, then validate per request:
private final OrderFieldsGuard orderFieldsGuard = OrderFieldsGuard.fromFields(WidgetOrderField.allowedNames());
// ...
orderFieldsGuard.requireAllowed(paging.getOrderByField());
The static OrderFields.requireAllowed is the one-off equivalent, taking the allow-list at the call site:
OrderFields.requireAllowed(paging.getOrderByField(), WidgetOrderField.allowedNames());
A null field means "no ordering" and is allowed.
Anything outside the allow-list is rejected with a HttpProblemCode.UnsupportedOrderField 400.
The idiomatic allow-list is an @Introspected enum of permitted fields:
@Introspected
public enum WidgetOrderField {
createdAt,
updatedAt,
name;
private static final Set<String> NAMES = Arrays.stream(values())
.map(Enum::name)
.collect(Collectors.toUnmodifiableSet());
public static Set<String> allowedNames() {
return NAMES;
}
}
Problem codes
HttpProblemCode provides the stable, machine-readable codes raised by these guards.
They are standard codes (STD- prefix, 2xxx sub-range) extending conta-problem-json.
| Constant | Code | Status | Raised by |
|---|---|---|---|
|
|
400 |
|
|
|
400 |
|
Rendering these as application/problem+json requires conta-problem-json-mn in the consuming application — see Getting started.