REST API checks
The REST API panel runs a fixed, zero-config ruleset against the host application's compiled web declarations: Spring MVC and Spring WebFlux controllers, or JAX-RS/Quarkus REST resource methods. It reports declaration conflicts and conditional design-review prompts, not a verdict on the application's runtime HTTP behavior.
There are 60 stable rule definitions across 8 categories, with 53 potentially emitting rules. Seven definitions (RAPI-MAP-008, RAPI-NAME-004, RAPI-ERR-011, RAPI-DOC-003, RAPI-VALID-005, RAPI-DTO-004, and RAPI-ERR-002) retain their IDs but always return SKIPPED: their evidence cannot establish an HTTP contract defect. Retired IDs are never reused, so saved dismissals keep their identity. The complete audit disposition ledger records the original 56 decisions, and the contract-defect audit records the later removals, fixes, and four additions.
Reading more than the preview
The ten-entry sampleViolations preview does not cap violationCount. View violations and GET <api>/rest-api/rules/{id}/violations?scanId=...&offset=0&limit=100 read bounded retained details from the same scan without re-importing declarations. Retention truncation is separate from declaration coverage; see snapshot, retention, and MCP/CLI retrieval.
Rules are registered in RestApiRuleRegistry and implemented by RestApiRules.java and the category rule classes in the framework-neutral bootui-engine module. MVC, WebFlux, and Quarkus share this catalogue; Spring-only checks use Spring declarations, including when both frameworks occur in the imported model.
What BootUI does
Missing required handler evidence qualifies usable known-findings scores. Retired and deliberately inapplicable rules do not themselves create coverage gaps. See the shared score eligibility policy.
The scanner resolves application base packages from Spring's AutoConfigurationPackages or Quarkus's build-time Jandex index, imports compiled classes with ArchUnit, and derives a bounded, read-only handler model. It records observable HTTP methods, paths, binding annotations, media declarations, response shapes, validation annotations, and declared exception types. Standard and custom JAX-RS @HttpMethod annotations are supported. Imports are limited to application packages, never an unbounded classpath scan.
Scanning is explicit and on demand. Reading the panel does not invoke application handlers, send requests, or start network work. The last report is cached. ArchUnit and resolvable application base packages are required for availability; the Spring starter supplies ArchUnit transitively.
Response, binding, and exception evidence
- Body and status are separate facts.
ResponseEntity<T>, JAX-RSResponse, and QuarkusRestResponse<T>can select status dynamically. SpringHttpEntity<T>carries headers/body, not status authority. Supported single-valued async forms such asMono<ResponseEntity<T>>,CompletionStage<ResponseEntity<T>>, andUni<RestResponse<T>>retain that distinction, as do inverse body-wrapper forms such asResponseEntity<Mono<T>>. A multi-valued outerFlux<ResponseEntity<T>>is not equivalent to a single response with a streaming body (Spring's single-response requirement). - Payload classification is bounded. Common async/reactive wrappers, including Spring async wrappers, Mutiny, and Kotlin coroutine wrappers, expose their resolvable payload or element type. Nested
void/Void/Unitin supported single-valued wrappers is no-body; a collection of nullable elements is not. Entity arrays retain their element type. Body-envelope detection includes nestedHttpEntity<Object>without giving it status authority. Streaming shape survives supported envelopes such asResponseEntity<Flux<T>>andRestResponse<Multi<T>>;ResponseEntity<List<T>>remains a collection, not a stream. Binarybyte[]is not a pageable resource collection. Direct SpringHttpHeadersis headers-only;HttpEntity<HttpHeaders>andResponseEntity<HttpHeaders>instead declare a serializable payload. Raw JAX-RSResponsehas an unknown body, not an inferred error DTO. - Declared statuses and headers are read on both stacks. Spring
@ResponseStatus(method, else class) and Quarkus REST's@ResponseStatus(int)are normalized to the same status names, and Quarkus REST's repeatable@ResponseHeadernames are recorded (Quarkus REST response properties). Like Quarkus itself, both Quarkus annotations are ignored on methods that returnResponseorRestResponse. An explicit Quarkus status overrides the JAX-RSvoid→ 204 default. - Imperative response arguments make the final response unknown. Servlet
ServletResponse,OutputStream,Writer, and reactiveServerHttpResponse/ServerWebExchangesignatures can write responses directly. Avoidreturn does not prove an empty wire response. The scanner does not inspect builder chains, filters, advice, or emitted bytes to recover their status, headers, or body. - Paths are declarations, not full route enumeration. Root/leaf composition preserves meaningful interior and trailing slashes. Spring type/method HTTP-method constraints combine by union. Required, explicitly named Spring path bindings are checked against each complete mapping alternative; optional/
Optionaland aggregate map bindings do not establish a missing-required-variable failure. Unresolved placeholders and unrooted JAX-RS subresource paths remain unknown. JAX-RS header/query parameter bindings are not dispatch conditions. - Validation annotations do not prove validation execution. Reactive request payloads are unwrapped for shape checks. Supported Spring validation/meta-
@Validateddeclarations are recognized conservatively; JAX-RS uses its own entity-validation semantics. Optional Spring primitivebooleanis not a numeric-null failure. Kotlin optional numeric bindings with unobservable source defaults remain unknown rather than guaranteed failures. - Exception declarations are not runtime resolution. Spring
@ExceptionHandler'svalueandexceptionaliases and Throwable-parameter inference are recognized. Explicit Quarkus mapper exception types take precedence over parameter fallback. Bounded hierarchy discovery includes registered inherited JAX-RS mappers and Spring's reactiveResponseEntityExceptionHandler. Exception-handler media types come from that declaration, not an unrelated controller@RequestMapping. Unknown error bodies do not count as contradictory known shapes.
Kotlin support reads bytecode by class name without adding a Kotlin runtime dependency. A supported suspend signature hides its compiler-supplied Continuation parameter and recovers the payload from its generic argument. Compiler-generated bridges and DTO accessors are excluded where recognized. This does not constitute complete Kotlin source reconstruction; arbitrary generic substitution and source-default recovery remain outside this model.
Complete, partial, and skipped analysis
The public report contains findings only: internal PASS, SKIPPED, and ERROR outcomes are not finding rows. An observed import, model, required-evidence extraction, or rule-evaluation failure produces scan.status = PARTIAL, with a bounded, sanitized explanation and any reliable findings retained. An import failure must not appear as a clean empty SCANNED report. A successfully analyzed application with no eligible controllers can still have no findings. Missing, empty, or malformed base-package names are rejected before importing and produce PARTIAL on an attempted scan; they never broaden the import to the classpath root. A successful import that finds no supported controllers remains SCANNED only when model extraction also completed. It records usable: false, coverageComplete: true, and no limitations, including when the imported class set is empty. This confirmed empty scope stays unscored and Not applicable, not incomplete or a fabricated 100, on MVC, WebFlux, and Quarkus. Failed or incomplete extraction remains incomplete even when no controllers were retained. Before an attempted scan, the initial report remains NOT_SCANNED and can explain a base-package discovery failure.
Incomplete extraction suppresses absence-based ERR-001 and ERR-009 conclusions while preserving reliable positive findings. An unresolved mapper type that the bounded model intentionally cannot resolve also makes ERR-009 SKIPPED, but that uncertainty alone is not an extraction failure and does not automatically make the scan PARTIAL.
Intentional inapplicability is different: retired emissions, unsupported framework facts, and genuinely unknown dynamic responses return SKIPPED where necessary and do not automatically make the scan partial. SCANNED means analysis completed within this bounded model, not that every runtime endpoint or behavior was enumerated.
Violation locations
Each finding about one handler method, exception handler or mapper, throwing endpoint, or controller carries that element's violation location, recorded from the same ArchUnit element the bounded model was built from: the method with its first recorded line, or the controller class. It works the same for Spring MVC, Spring WebFlux, JAX-RS, and Kotlin controllers. Findings that name several handlers (RAPI-MAP-002 duplicate routes, RAPI-ERR-010 conflicting error-body categories) and application-wide findings (versioning, pagination vocabulary, missing error handling) carry no location. A RAPI-VER-007 finding caused by a class-level consumes carries the controller's location. The source path is resolved during the explicit scan through the Architecture advisor's module and source-set lookup; finding text, counts, and status never depend on it.
What BootUI does not do
- It does not modify, compile, instrument, or execute application code during a scan.
- It does not check authentication, authorization, or CORS; these remain Security panel concerns.
- It excludes MicroProfile
@RegisterRestClientinterfaces, which represent outbound clients rather than inbound resources. - It does not inspect actual response content, validation execution, database result limits, caching policy, retry deduplication, generated OpenAPI documents, or headers supplied elsewhere.
- It does not resolve every Spring composed/inherited mapping, functional endpoint, runtime route registration, arbitrary generic hierarchy, or dynamic JAX-RS subresource locator graph.
- It does not infer omitted parameter names from unavailable source metadata or interpret unresolved
${...}and#{...}path expressions as literal naming violations. - It does not replace contract tests or scope/selector/precedence-aware exception resolution. The separate declared error-contract catalogue is not a claim that the REST rule model resolves every runtime exception.
Severity scale
Findings use HIGH, MEDIUM, LOW, and INFO; CRITICAL is supported but unused here. Severity includes evidence confidence: a mutation-like method name is weaker evidence than a conflicting required path binding.
| Scope | HIGH | MEDIUM | LOW | INFO | Total |
|---|---|---|---|---|---|
| Stable definitions, including retired emissions | 8 | 8 | 20 | 24 | 60 |
| Potentially emitting rules | 7 | 8 | 17 | 21 | 53 |
The documentation checks are gated by the optional OpenAPI integration: Swagger/springdoc annotation availability on Spring or MicroProfile OpenAPI on Quarkus. Both annotation families are recognized without making either dependency mandatory. Missing annotations are not proof that generated or static documentation is absent.
The shared advisor score weights concrete findings, not just violated rule IDs; dismissing a rule removes its findings from that calculation. SCANNED and PARTIAL reports can score usable observed evidence, while complete-empty scope remains unscored. Coverage gaps remain in scan notes; the penalty formula is unchanged.
Filter the list, then jump to a check — the detail below narrows to match.
RAPI-MAP-001MEDIUMUse HTTP-method-specific mappingsRAPI-MAP-002HIGHNo duplicate route mappingsRAPI-MAP-003LOWReview mutation-like names on GET handlersRAPI-MAP-004LOWPrefer a class-level base pathRAPI-MAP-005INFOReview trailing and doubled slashesRAPI-MAP-006HIGHRequired Spring path bindings match each pathRAPI-MAP-007MEDIUMReview request entities on GET/HEAD/DELETERAPI-MAP-008LOWMutating item methods target an identified resourceRAPI-MAP-009HIGHNo duplicate Spring path-variable tokensRAPI-MAP-010INFOReview catch-all REST mappingsRAPI-MAP-011INFOReview deeply nested resource pathsRAPI-NAME-001INFOConsider noun-oriented resource pathsRAPI-NAME-002INFOConsider plural collection namesRAPI-NAME-003INFOConsider lowercase kebab-case pathsRAPI-NAME-004LOWNo format-extension suffixes in path segmentsRAPI-RESP-001LOWReview the default status of creation-like POST handlersRAPI-RESP-002LOWReview default empty DELETE responsesRAPI-RESP-003LOWPrefer informative response-envelope body typesRAPI-RESP-004INFOConsider structured read representationsRAPI-RESP-005LOWReview default no-body GET declarationsRAPI-RESP-006MEDIUM204 declarations must not promise contentRAPI-RESP-007MEDIUMReview method-level status and response-envelope overlapRAPI-RESP-008INFOConsider discoverability for declared 201 responsesRAPI-RESP-009INFOReview dedicated HEAD handler efficiencyRAPI-RESP-010MEDIUMDo not set @ResponseStatus reason on body-returning REST handlersRAPI-RESP-011LOWMap an empty Optional read to an explicit statusRAPI-VALID-001LOWReview request-payload cascade validationRAPI-VALID-002HIGHAvoid binding requests directly to JPA entitiesRAPI-VALID-003MEDIUMOptional Spring numeric parameters need a nullable/defaulted bindingRAPI-VALID-004LOWReview aggregate query-map contractsRAPI-VALID-005INFOConsider retry deduplication for creation-like POSTsRAPI-VALID-006HIGHBind at most one @RequestBody per handlerRAPI-DTO-001HIGHAvoid persistence entities in responsesRAPI-DTO-002LOWPrefer informative response body typesRAPI-DTO-004INFOConsider immutable response DTOsRAPI-DTO-005LOWConsider java.time in response DTOsRAPI-PAGE-001LOWReview collection reads without visible paginationRAPI-PAGE-002LOWPreserve paging metadata for Pageable handlersRAPI-PAGE-003INFOReview pagination vocabulary differencesRAPI-VER-001INFOReview absent or uneven version signalsRAPI-VER-002INFOConsider explicit consumes declarationsRAPI-VER-003INFOReview wildcard media rangesRAPI-VER-004INFOState the PATCH document formatRAPI-VER-005LOWReview inconsistent produces declarationsRAPI-VER-006INFOReview mixed versioning strategiesRAPI-VER-007HIGHBodyless handlers must not require a Content-TypeRAPI-ERR-001INFOReview application-wide exception handling declarationsRAPI-ERR-002LOWPrefer informative throws declarationsRAPI-ERR-003INFOConsider Spring ProblemDetail convenience typesRAPI-ERR-004MEDIUMMake error-handler status ownership explicitRAPI-ERR-005LOWReview broad handlers with fixed non-5xx statusesRAPI-ERR-006INFOReview mixed Spring error-declaration approachesRAPI-ERR-007INFOConsider Retry-After for declared 429/503 statusesRAPI-ERR-008LOWConsider structured error bodiesRAPI-ERR-009MEDIUMReview declared exceptions without a mapping in the modelRAPI-ERR-010LOWReview differing known error contractsRAPI-ERR-011HIGHException handlers do not expose stack tracesRAPI-DOC-001INFOConsider explicit operation documentationRAPI-DOC-002INFOConsider explicit operation groupingRAPI-DOC-003INFODeprecated endpoints signal deprecation to HTTP clients
Routing & HTTP method mapping
RAPI-MAP-001 - Use HTTP-method-specific mappings
- Severity: MEDIUM
- Detects: A Spring mapping with no HTTP-method constraint after combining type and method declarations. JAX-RS verb declarations are not subject to this Spring-specific recommendation.
- Recommendation: State the accepted verbs with a composed mapping or
@RequestMapping(method = ...). An unconstrained mapping does not itself prove a state-changing GET. - Learn more: Spring mapping conditions.
RAPI-MAP-002 - No duplicate route mappings
- Severity: HIGH
- Detects: Exact duplicate observable dispatch conditions, including path, HTTP method, media types, and applicable Spring params/headers/version conditions. Meaningful slash differences are preserved. JAX-RS query/header bindings do not disambiguate routes; incomplete subresource paths are not compared as complete routes.
- Spring scope: Spring MVC and WebFlux reject identical mappings at startup ("Ambiguous mapping"), so a running application can only contain them when one controller is inactive, such as a profile- or condition-specific alternative; those pairs are not reported. Mappings that differ but share one path alternative or HTTP method (for example
{"/a", "/b"}and{"/a", "/c"}, orGET,POST /xandGET /x) register successfully and then fail each matching request with "Ambiguous handler methods" (500); those are reported. Quarkus REST rejects JAX-RS duplicates at startup by default (quarkus.rest.fail-on-duplicate=true), so a JAX-RS finding means that check is disabled or one resource is a build-time alternative. - Recommendation: Give each exact dispatch combination one handler. This is not a complete overlap or ambiguity detector.
- Learn more: Spring mapping conditions; Spring handler registration; Jakarta REST matching; Quarkus duplicate-endpoint check.
RAPI-MAP-003 - Review mutation-like names on GET handlers
- Severity: LOW
- Detects: A GET handler with a create/update/delete/save-style name. Ambiguous prefixes such as
postProcess,putAside, andpatchVersionare excluded. A name is not evidence that a write executes. - Recommendation: Review requested effects against GET's safety requirement; use a mutating HTTP method if the operation really changes resource state. Incidental logging does not make a safe method unsafe.
- Learn more: RFC 9110 §9.2.1.
RAPI-MAP-004 - Prefer a class-level base path
- Severity: LOW
- Detects: Spring controller mapping alternatives repeat a common leading segment without a type-level base path. All alternatives must support that conclusion. Controller interfaces, including generated spec-first interfaces, are exempt; hand-written interfaces receive the same exemption.
- Recommendation: Optionally hoist the common prefix to the class. This is a maintainability preference, not HTTP correctness.
- Learn more: Spring mapping declarations.
RAPI-MAP-005 - Review trailing and doubled slashes
- Severity: INFO
- Detects: Raw mapping declarations contain trailing or doubled slashes.
- Recommendation: Choose a deliberate path convention. Slash variants can identify distinct paths; the style check does not normalize route identity or transfer Spring's matching behavior to JAX-RS.
- Learn more: Spring path matching.
RAPI-MAP-006 - Required Spring path bindings match each path
- Severity: HIGH
- Detects: An explicitly named, required Spring
@PathVariableis absent from a complete individual mapping alternative. Optional/Optional, aggregate map bindings, and unresolved paths do not establish this failure. - Recommendation: Correct the token or binding, or deliberately make the binding optional for alternatives without that token. JAX-RS binding/default semantics do not imply Spring's missing-required-variable error.
- Learn more: Spring
PathVariable.required; Jakarta RESTPathParam.
RAPI-MAP-007 - Review request entities on GET/HEAD/DELETE
- Severity: MEDIUM
- Detects: A declared request entity on GET, HEAD, or DELETE.
- Recommendation: Prefer query/path parameters or an appropriate body-oriented operation for interoperable APIs. RFC 9110 gives this content no generally defined semantics; it is not a categorical prohibition. A private client/server agreement may be intentional but does not establish support by intermediaries.
- Learn more: GET, HEAD, and DELETE in RFC 9110.
RAPI-MAP-008 - Mutating item methods target an identified resource
- Severity: LOW (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. A literal URI such as/configurationalready identifies a resource. Absence of{id}cannot establish accidental collection-wide mutation. - Recommendation: Review resource semantics in the API contract, not through an English singleton/bulk allowlist. The rule and dismissal ID remain reserved for this original concern.
- Learn more: RFC 5789's literal-URI PATCH example.
RAPI-MAP-009 - No duplicate Spring path-variable tokens
- Severity: HIGH
- Detects: Repeated capture names in a Spring path template, which its parser rejects.
- Recommendation: Use distinct Spring token names, such as
{userId}and{orderId}. JAX-RS is skipped: repeated scoped names bind the latest occurrence rather than following Spring's parser rule. - Learn more: Spring path parser; Jakarta REST
PathParam.
RAPI-MAP-010 - Review catch-all REST mappings
- Severity: INFO
- Detects: Spring
/**or{*path}, or JAX-RS regex catch-alls such as{path:.*}and{path:.+}. A constrained token such as{id:[0-9]+}is not a catch-all. - Recommendation: Review whether a broad routing surface is intentional. A catch-all does not prove shadowing of more-specific routes or that typos return 200.
- Learn more: Spring pattern specificity; Jakarta REST matching.
RAPI-MAP-011 - Review deeply nested resource paths
- Severity: INFO
- Detects: More than three visible collection/
{id}pairs in a known path. - Recommendation: Consider a flatter resource structure if it improves usability. Three levels is this heuristic's style threshold, not an HTTP limit; an unknown root cannot establish full nesting depth.
- Learn more: RFC 9110 resource identification (no three-level requirement).
Naming & resource design
RAPI-NAME-001 - Consider noun-oriented resource paths
- Severity: INFO
- Detects: Action-like literal segments, using an English-name heuristic. Ambiguous
post,put, andpatchare flagged only in forms such as/postMessage, not a noun-like/blog/post/{id}. - Recommendation: Prefer nouns where useful, while allowing intentional command/action resources. Verb spelling does not violate HTTP or by itself establish poor resource design.
- Learn more: RFC 9110 resource identification (no noun-only grammar).
RAPI-NAME-002 - Consider plural collection names
- Severity: INFO
- Detects: A known collection-shaped response with a singular-looking endpoint name, excluding recognized uncountable/collective words such as
history,inventory,staff, andnews. - Recommendation: Use a consistent vocabulary appropriate to the API's language. Return shape and English spelling do not prove runtime cardinality; binary bodies are not resource collections.
- Learn more: RFC 9110 resource identification (pluralization is optional style).
RAPI-NAME-003 - Consider lowercase kebab-case paths
- Severity: INFO
- Detects: Literal camelCase, snake_case, or uppercase path segments; unresolved expressions are excluded.
- Recommendation: Choose a consistent convention. Case-sensitive paths and non-kebab spellings are legitimate, not protocol defects.
- Learn more: RFC 3986 §6.2.2.1.
RAPI-NAME-004 - No format-extension suffixes in path segments
- Severity: LOW (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. Explicit/export.jsonand/schema.xmlmappings are valid. Removal of implicit suffix matching does not invalidate literal dotted routes. - Recommendation: Choose explicit representations or negotiated media types deliberately; no migration is inferred merely from a filename suffix. The dismissal ID remains unchanged.
- Learn more: Spring 7.0.9 literal dotted-path tests.
Status codes & responses
RAPI-RESP-001 - Review the default status of creation-like POST handlers
- Severity: LOW
- Detects: A creation-like method name on a POST with an apparent default 200, not an explicitly chosen status. Dynamic status envelopes and imperative response arguments prevent that conclusion.
- Recommendation: Use 201 for completed creation where appropriate; 202 can represent accepted asynchronous work. Creation intent is inferred from a name, and Location is conditional guidance rather than universally required.
- Learn more: RFC 9110 POST and 201 Created.
RAPI-RESP-002 - Review default empty DELETE responses
- Severity: LOW
- Detects: A Spring DELETE with a no-body return and apparent default status, excluding explicit statuses, dynamic status envelopes, and direct response-writing arguments.
- Recommendation: Consider an explicit 204 when deletion completed without a representation. Explicit 202 and other deliberate statuses are not accidental defaults. JAX-RS void methods already default to 204.
- Learn more: RFC 9110 DELETE; Jakarta REST return semantics.
RAPI-RESP-003 - Prefer informative response-envelope body types
- Severity: LOW
- Detects: A supported body envelope has a raw, wildcard, or
Objectbody contract, including resolvable single-value async envelopes and nestedHttpEntity<Object>. Body-envelope presence does not imply status authority. Plain non-generic JAX-RSResponseis not a raw Spring generic. - Recommendation: Supply a concrete DTO type where practical, or document a deliberate dynamic schema. Generic erasure limits inference but does not prove an undocumented API.
- Learn more: Spring response envelopes; OpenAPI schemas.
RAPI-RESP-004 - Consider structured read representations
- Severity: INFO
- Detects: A GET with a known bare String or primitive body, without an explicit
text/*media declaration. - Recommendation: Consider a DTO for an evolving contract. Scalars are valid HTTP/JSON representations, and a structured envelope is optional.
- Learn more: RFC 9110 representations.
RAPI-RESP-005 - Review default no-body GET declarations
- Severity: LOW
- Detects: A Spring GET with a resolvable no-body result and apparent default status. Explicit statuses, dynamic envelopes, and imperative response arguments are excluded; nested single-value Void/Unit is recognized.
- Recommendation: Confirm that a bodyless read is intended, and declare the desired status or representation. This does not prove an empty wire response. JAX-RS void's default 204 is not reported as Spring's default 200.
- Learn more: Spring return handling; Jakarta REST return semantics.
RAPI-RESP-006 - 204 declarations must not promise content
- Severity: MEDIUM (was HIGH)
- Detects: A declared 204 with a content-capable return, where neither a dynamic status envelope nor an imperative response path makes the conclusion unknown. Plain
HttpEntity<T>does not override annotation status; supported no-body wrappers and headers-only results are not content-capable. - Recommendation: Align the declared return with 204's no-content requirement, or choose a content-bearing status. The finding concerns contradictory declarations, not proof of transmitted forbidden bytes or a non-null result. Tomcat and Reactor Netty drop content on 204, so clients never see the serialized body; the cost is lost intent, wasted serialization, and a generated schema that is never sent, which is why the severity is MEDIUM.
- Learn more: RFC 9110 §15.3.5.
RAPI-RESP-007 - Review method-level status and response-envelope overlap
- Severity: MEDIUM
- Detects: A Spring method-level
@ResponseStatuscombined with a status-bearingResponseEntity, including supported single-value async wrapping.HttpEntityis not status-bearing; class-level defaults are not flagged. - Recommendation: Review status ownership and the native framework's dispatch behavior. In normal entity handling the envelope selects status; a
@ResponseStatus(reason = ...)error-response path can short-circuit processing. The annotation is not universally ignored or redundant, so blanket removal is not the recommendation. - Learn more: Spring
ResponseStatus; MVC entity processing.
RAPI-RESP-008 - Consider discoverability for declared 201 responses
- Severity: INFO
- Detects: A plain-body declared 201 without a visible header-setting response path, as an optional review prompt.
- Recommendation: Consider Location when a newly created resource differs from the target URI. RFC 9110 uses the target URI when Location is absent. Filters/advice may add headers; this is not a missing-Location finding.
- Learn more: RFC 9110 §15.3.2.
RAPI-RESP-009 - Review dedicated HEAD handler efficiency
- Severity: INFO
- Detects: A dedicated HEAD handler with a known content-capable declaration. Shared GET/HEAD mappings, headers-only results, no-body wrappers, and Unit are excluded.
- Recommendation: Avoid unnecessary body construction when metadata alone suffices, or let the framework derive HEAD from GET. Framework suppression means the signature does not prove content is sent on the wire.
- Learn more: RFC 9110 HEAD; Spring HEAD suppression.
RAPI-RESP-010 - Do not set @ResponseStatus reason on body-returning REST handlers
- Severity: MEDIUM
- Detects: A Spring MVC REST handler or body-rendering
@ExceptionHandlerwith a content-capable return whose effective@ResponseStatus(method-level, else class-level) sets a non-blankreason. Spring MVC then callsHttpServletResponse.sendError(status, reason)and returns before return-value handling: the returned body, even aResponseEntityorProblemDetail, is discarded and the container or Boot error response is written instead. Exception classes annotated with@ResponseStatus(reason = ...)are a separate, supported pattern and are not in scope. - Not evaluated: Spring WebFlux applies the status, ignores the reason, and still writes the body, so the rule is
SKIPPEDthere. When the adapter cannot tell the active request stack, the rule isSKIPPEDas missing evidence. JAX-RS has no equivalent attribute. On Spring MVC, a handler reported here is not reported again by RAPI-RESP-007. - Recommendation: Remove the
reason; return aResponseEntity, or aProblemDetailwhosedetailcarries the message. Spring's own documentation callsreasonunsuitable for REST APIs. - Learn more: Spring
ResponseStatus; MVC reason handling; WebFlux status handling.
RAPI-RESP-011 - Map an empty Optional read to an explicit status
- Severity: LOW
- Detects: A Spring GET handler whose body is
java.util.Optional<T>, directly or inside a supported single-value async wrapper such asCompletableFuture<Optional<T>>. AnOptionalinsideResponseEntityis the application's own status decision and is excluded, as are handlers that write the response imperatively. - Why: Spring has no return-value handling that turns an empty
Optionalinto 404: on MVC and WebFlux the client receives 200 with an empty or JSONnullbody. Returning a repositoryfindById(...)result directly is the usual cause. An API can intentionally answer 200 withnull, so this is LOW rather than a defect claim. - Recommendation: Return
ResponseEntity.of(optional)or throw a not-found error so absence maps to 404, or document the 200-with-null contract. JAX-RS is not evaluated. - Learn more: Spring
ResponseEntity.
Input validation & binding
RAPI-VALID-001 - Review request-payload cascade validation
- Severity: LOW
- Detects: A complex request payload without a recognized cascade-validation declaration, including payloads inside supported reactive wrappers. Spring annotation conventions are not imposed on JAX-RS.
- Recommendation: If DTO field constraints should cascade, use the framework's supported validation trigger. Missing
@Validdoes not prove unchecked input: direct constraints and programmatic validation may be intentional, and a cascade annotation alone does not prove constraints exist or execute. - Learn more: Spring validation annotation recognition; Jakarta REST entity validation.
RAPI-VALID-002 - Avoid binding requests directly to JPA entities
- Severity: HIGH
- Detects: A resolvable request payload, including supported reactive payloads, is a JPA entity.
- Recommendation: Consider a request DTO and explicit mapping to reduce persistence coupling and over-posting risk. The signature does not prove every persistent field is writable or that any actual disclosure occurred.
- Learn more: Spring data-binding design guidance.
RAPI-VALID-003 - Optional Spring numeric parameters need a nullable/defaulted binding
- Severity: MEDIUM
- Detects: An optional Java numeric primitive Spring binding with no nonblank default. Omission cannot bind null, and a literal empty/blank default can fail numeric conversion even when
requiredretains its annotation default. Primitive boolean is excluded because Spring supplies false; uncertain Kotlin source-default cases are not reported as guaranteed numeric failures. - Recommendation: For the applicable Java numeric case, use a boxed type or an explicit default. This does not transfer Spring's resolver behavior to JAX-RS.
- Learn more: Spring named-value resolver; WebFlux named-value resolver.
RAPI-VALID-004 - Review aggregate query-map contracts
- Severity: LOW
- Detects: An unnamed Spring
@RequestParam Map/MultiValueMapaggregate binding. An explicitly named map uses conversion for that parameter and is not classified as binding every query parameter. - Recommendation: Consider typed parameters or an explicit allowlist/schema. Maps can be documented and validated; this signature alone does not establish those policies.
- Learn more: Spring request parameters.
RAPI-VALID-005 - Consider retry deduplication for creation-like POSTs
- Severity: INFO (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. A creation-like method name without anIdempotency-Keybinding cannot establish unsafe retries: filters, gateways, natural keys, and application logic deduplicate where the scanner cannot see. The finding fired on almost every creation endpoint, so it was noise rather than a review aid. - Recommendation: Review retry deduplication in the API design where duplicate creation matters. The dismissal ID remains unchanged.
- Learn more: Idempotency-Key draft history.
RAPI-VALID-006 - Bind at most one @RequestBody per handler
- Severity: HIGH
- Detects: A Spring handler with two or more
@RequestBodyparameters. The request body is one stream: on Spring MVC the second binding finds it already consumed and every request fails with 400 "Required request body is missing" (or the second parameter receivesnullwhen optional). WebFlux request bodies can be consumed only once. Handlers whose only consumes media type isapplication/x-www-form-urlencodedare excluded, because Spring MVC rebuilds form bodies from the parsed request parameters. - Not evaluated: JAX-RS allows one entity parameter, and Quarkus REST rejects several at deployment.
- Recommendation: Bind one request DTO that composes the parts, for example a record holding both objects, or use
@RequestPartfor multipart requests. - Learn more: Spring
@RequestBody.
DTO & payload contracts
RAPI-DTO-001 - Avoid persistence entities in responses
- Severity: HIGH
- Detects: A known response payload or collection/array element is a JPA entity, including supported wrapped returns.
- Recommendation: Consider DTOs to isolate persistence structure and serialization side effects. Lazy loads and internal-field exposure are risks, not observed queries or disclosures; serializer policy is not inspected.
- Learn more: Spring response-body handling.
RAPI-DTO-002 - Prefer informative response body types
- Severity: LOW
- Detects: A known body uses Map, Object, or JsonNode, including Jackson 2's
com.fasterxml.jacksonand Jackson 3'stools.jacksontype names. - Recommendation: Prefer a typed DTO when it improves inference, or explicitly document the dynamic schema. OpenAPI can describe arbitrary objects; these declarations do not prove missing documentation.
- Learn more: OpenAPI Schema Object.
RAPI-DTO-004 - Consider immutable response DTOs
- Severity: INFO (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. Public setters on a response type do not change the serialized HTTP contract; DTO mutability is general code style, which belongs to code-hygiene review such as the Architecture advisor rather than to REST API design. - Recommendation: Choose records or mutable DTOs by code convention. The dismissal ID remains unchanged.
- Learn more: Java record classes.
RAPI-DTO-005 - Consider java.time in response DTOs
- Severity: LOW
- Detects: Response DTO fields use
java.util.DateorCalendar. This rule does not inspect request-only DTO fields. - Recommendation: Consider
Instant,LocalDate, or another appropriatejava.timetype for explicit temporal semantics. This is not proof of serializer failure, nor does Boot rewrite application field types. - Learn more: Java date/time API.
Pagination & collections
RAPI-PAGE-001 - Review collection reads without visible pagination
- Severity: LOW
- Detects: A collection-shaped GET without recognized paging inputs such as Pageable, page/size, limit/offset, or cursor/after/before. Binary bodies and streaming return types with explicit SSE, NDJSON, or JSON-sequence media are excluded, including streams inside supported response envelopes. An envelope-wrapped List remains a collection and is not exempt merely because its outer type is ResponseEntity.
- Recommendation: Confirm bounds or a streaming contract. A collection declaration cannot prove an unbounded database load; limits may be fixed or implemented elsewhere. A reactive wrapper alone does not establish streaming.
- Learn more: Spring Data web/paging support.
RAPI-PAGE-002 - Preserve paging metadata for Pageable handlers
- Severity: LOW
- Detects: A Spring handler accepts Pageable but returns a plain collection/array rather than a known paging representation. Unknown JAX-RS paging-envelope conventions are
SKIPPED. - Recommendation: Return a stable pagination envelope, such as a suitable PagedModel, or document header links. Do not expose PageImpl serialization as a universal fix: Spring Data warns its representation is unstable, and Slice does not promise total counts.
- Learn more: Spring Data stable page representations.
RAPI-PAGE-003 - Review pagination vocabulary differences
- Severity: INFO
- Detects: Recognized page/size, offset/limit, or cursor/after/before families differ across handlers. Classification selects a family by priority rather than modeling every simultaneous pagination mode.
- Recommendation: Prefer consistency where workloads are comparable; different pagination strategies can be deliberate. HTTP does not mandate one vocabulary.
- Learn more: Pagination design guidance (optional style).
Versioning & content negotiation
RAPI-VER-001 - Review absent or uneven version signals
- Severity: INFO
- Detects: No recognized version signal, or signals on only some handlers. Signals include
/vN, positive version-header/query declarations, genuinely versioned media types, and Spring version conditions. An unversioned vendor media type or a negated condition is not evidence of versioning. - Recommendation: Choose a versioning policy if the API needs one. The Spring integration also considers supported versioning configuration for the actual active MVC or WebFlux application context; inactive-stack properties, arbitrary
use.*keys, and configuration presence alone are not proof of a working resolver. JAX-RS bindings are version hints, not dispatch constraints. Operational, authentication, and generated-documentation endpoints (/actuator,/login, anyapi-docsorswagger-uisegment, and similar) are excluded; a leading/v3is an ordinary version segment, not documentation. - Learn more: Boot MVC version properties; Boot WebFlux version properties.
Boot 4.1.1 declares separate spring.mvc.apiversion and spring.webflux.apiversion namespaces. The integration selects the latter for an active ReactiveWebApplicationContext, not merely because WebFlux classes or properties exist. Within the selected namespace, supported is a list of strings, default, use.header, and use.query-parameter are strings, use.path-segment is an integer, and use.media-type-parameter maps media types to parameter names. For example, spring.webflux.apiversion.use.media-type-parameter[application/json]=v declares a media-type-parameter resolver input. use.path and use.media-type are not the Boot 4.1.1 property names. These configuration facts are bounded versioning hints, not a runtime request proving that version selection works.
RAPI-VER-002 - Consider explicit consumes declarations
- Severity: INFO (was LOW)
- Detects: A POST/PUT/PATCH request-entity declaration without an explicit consumes constraint.
- Recommendation: Declare supported formats on the body-accepting method when useful to the contract. Absence of consumes does not make every media type readable: Spring converters/readers and JAX-RS providers still constrain decoding, while an explicit consumes narrows and documents acceptance. On Spring, do not hoist consumes to the class level: GET/HEAD/DELETE handlers would then reject requests without
Content-Type(RAPI-VER-007). It is INFO because the declaration is optional explicitness, and the previous LOW weighting penalized almost every POST. - Learn more: Spring media mapping; Jakarta REST media declarations.
RAPI-VER-003 - Review wildcard media ranges
- Severity: INFO
- Detects: Broad consumes/produces ranges such as
*/*orapplication/*. - Recommendation: Prefer concrete types when broad matching is unintended. Wildcards, including structured-suffix ranges where supported, are legitimate negotiation behavior, not disabled negotiation.
- Learn more: RFC 9110 Accept; Jakarta REST media declarations.
RAPI-VER-004 - State the PATCH document format
- Severity: INFO
- Detects: Missing or broad PATCH consumes declarations, not merely a non-JSON format.
- Recommendation: State a concrete supported format and its semantics. JSON Patch, Merge Patch, documented plain JSON, XML, vendor, binary, and parameterized concrete media types can all be intentional. RFC 5789 requires no single default patch format.
- Learn more: RFC 5789 §2 and its non-JSON example.
RAPI-VER-005 - Review inconsistent produces declarations
- Severity: LOW
- Detects: Some body-producing handlers in a controller declare produces while others omit it. No-body, headers-only, and unknown imperative responses do not establish a missing body-media declaration.
- Recommendation: Consider consistent explicit media declarations. Writers/providers can still select supported formats without them, so this is not proof of an inconsistent wire contract.
- Learn more: Spring media mapping; Jakarta REST media declarations.
RAPI-VER-006 - Review mixed versioning strategies
- Severity: INFO
- Detects: Different recognized path, header, query-parameter, or versioned-media signals across handlers, using the same positive-signal classification as VER-001. Header and query parameters are separate transport strategies; vendor prefixes alone do not count.
- Recommendation: Prefer a coherent policy where appropriate. Spring's
versioncondition is not a separate transport strategy from its configured resolver. Binding hints do not prove runtime version selection. - Learn more: Spring API versioning.
RAPI-VER-007 - Bodyless handlers must not require a Content-Type
- Severity: HIGH
- Detects: A Spring handler mapped to GET, HEAD, or DELETE that binds no request body (no
@RequestBody,HttpEntity,RequestEntity,InputStream,Reader, or@RequestPartparameter) but whose effective consumes condition (method-level, which replaces class-level) has no expression matchingapplication/octet-stream. Spring'sConsumesRequestConditionwaives its check for requests without a body only when a@RequestBody(required = false)parameter says so; otherwise a missingContent-Typeis matched asapplication/octet-stream, and ordinary GET requests receive 415 on both Spring MVC and WebFlux. The usual cause is a class-level@RequestMapping(consumes = ...)meant for the write methods; that case is reported once per controller, listing the affected handlers. - Not reported:
*/*,application/*,application/octet-stream, or negated expressions that admit it; unresolved${...}expressions; and content-type dispatch, where another handler for the same path and method has a different (or no) consumes condition and therefore serves requests withoutContent-Type. - Not evaluated: JAX-RS selects resource methods with a different algorithm.
- Recommendation: Declare consumes on the methods that read a body (POST/PUT/PATCH) instead of at class level, or remove it from GET/HEAD/DELETE handlers.
- Learn more: Spring consumable media types; MVC consumes matching.
Error handling & documentation
RAPI-ERR-001 - Review application-wide exception handling declarations
- Severity: INFO
- Detects: Controllers exist but no recognized application-wide advice/mapper declaration is found. Discovery includes Spring MVC/WebFlux ResponseEntityExceptionHandler advice, registered inherited JAX-RS ExceptionMapper types, and Quarkus ServerExceptionMapper declarations.
- Not evaluated: Incomplete extraction cannot support an absence finding. With a true aggregate handling flag, mixed Spring/JAX-RS declarations are also
SKIPPED: the flag cannot prove application-wide handling separately for each stack. Pure JAX-RS with a true flag but no mapper declarations is likewise unknown. Recognized inherited Spring reactive advice can pass without enumerated exception methods; a false aggregate flag in an otherwise complete mixed-framework model can still support the absence-review prompt. - Recommendation: Review the intended error policy. Application-wide advice is optional; framework defaults and local handlers may already be sufficient. Absence from this model does not prove absence of error handling.
- Learn more: Spring reactive exception advice; Jakarta REST exception mapping.
RAPI-ERR-002 - Prefer informative throws declarations
- Severity: LOW (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. A Javathrowsclause influences neither Spring nor Jakarta REST exception resolution nor the HTTP error contract, sothrows Exceptionis general Java style rather than an API finding. The declared exception types are still modeled for RAPI-ERR-009. - Recommendation: Map failures through exception handlers or mappers. The dismissal ID remains unchanged.
- Learn more: Java throws clauses.
RAPI-ERR-003 - Consider Spring ProblemDetail convenience types
- Severity: INFO
- Detects: Informative Spring error-body declarations use alternative shapes rather than known ProblemDetail/ErrorResponse types. View, no-body, dynamic, and unknown returns do not establish this comparison; JAX-RS is skipped because arbitrary DTO schema compliance is not observable.
- Recommendation: Consider Spring's convenience types if adopting RFC 9457. Problem Details is optional, and a custom DTO can implement it; neither type choice nor a return signature proves wire conformance.
- Learn more: RFC 9457.
RAPI-ERR-004 - Make error-handler status ownership explicit
- Severity: MEDIUM
- Detects: An applicable Spring body-rendering exception handler has no observable explicit error-status path. Supported async status envelopes, error-response types, native response arguments, and no-body results are distinguished; unknown error-body shapes are excluded, and plain HttpEntity does not supply status.
- Recommendation: Declare the intended status through an appropriate annotation, response envelope, or error type. Unknown Quarkus mapper semantics are skipped rather than assigned Spring's default 200.
- Learn more: Spring exception handling; Jakarta REST exception mapping.
RAPI-ERR-005 - Review broad handlers with fixed non-5xx statuses
- Severity: LOW
- Detects: A broad Exception/Throwable handler has a known fixed non-5xx status without a dynamic status or imperative response path overriding that inference.
- Recommendation: Review whether unrelated failures should share that status. A sole ResponseEntity/Response mapper can choose many statuses; a sole 500 fallback does not prove all errors collapse into one response.
- Learn more: Spring reactive exception advice; RFC 9110 server errors.
RAPI-ERR-006 - Review mixed Spring error-declaration approaches
- Severity: INFO
- Detects: The application uses Spring ProblemDetail support and a
@ResponseStatusexception class actually appears in a handler's declared throws, has known ancestry, and has no declared Spring handler covering that class, an ancestor, or broad Exception. Unused or possibly covered annotations do not establish this finding; uncertain cases and JAX-RS are skipped. - Recommendation: Consider ErrorResponseException if it simplifies a deliberate Spring error policy. Advice may already translate annotated exceptions to problem documents; coexistence does not prove inconsistent payloads, and RFC 9457 mandates no Spring type.
- Learn more: Spring error responses; RFC 9457.
RAPI-ERR-007 - Consider Retry-After for declared 429/503 statuses
- Severity: INFO
- Detects: An observable declared 429 or 503 offers a retry-policy review opportunity, excluding dynamic status paths that prevent treating the annotation as the effective status.
- Recommendation: Consider Retry-After where a useful retry time is known. It is optional, and headers may be added imperatively or elsewhere; this scanner cannot prove Retry-After is absent.
- Learn more: RFC 9110 §10.2.3; RFC 6585 §4.
RAPI-ERR-008 - Consider structured error bodies
- Severity: LOW
- Detects: A known body-rendering exception handler returns a raw String, not a view name or unknown body.
- Recommendation: Consider a typed error DTO or Problem Details if clients need stable fields. Intentional text errors are valid; this is not an RFC 9457 conformance failure.
- Learn more: RFC 9457; Spring exception handling.
RAPI-ERR-009 - Review declared exceptions without a mapping in the model
- Severity: MEDIUM
- Detects: An endpoint's declared application exception has no matching handler/mapper or applicable status-annotated exception declaration in the imported model; recognized supertypes count. Alias, inferred, explicit mapper, and bounded inherited exception types participate. The rule stays silent if no exception handlers are declared, leaving the broad review to ERR-001.
- Not evaluated: Incomplete extraction or any imported handler/mapper with unresolved handled-exception types prevents an absence-based finding. The latter is an intentional
SKIPPEDlimitation, not automaticallyPARTIAL; an observed required-evidence extraction failure is what makes the scan partial. - Recommendation: Confirm the intended error mapping. The global declaration comparison is not scope-, selector-, or precedence-aware and cannot prove runtime fall-through, successful resolution, or absence of failures.
- Learn more: Spring exception handling; Jakarta REST exception mapping.
RAPI-ERR-010 - Review differing known error contracts
- Severity: LOW
- Detects: Informative body-rendering exception declarations differ in known body categories under compatible or unspecified media types. Dynamic Map, Object, JAX-RS Response, raw envelopes, JsonNode, and other unknown shapes do not count as an incompatible second contract. Spring error media evidence comes from
@ExceptionHandleritself. - Recommendation: Confirm clients can handle the intended formats. Negotiated representations can legitimately differ: distinct negotiation media alone no longer produce a finding. Category comparison does not inspect fields, emitted content, or complete runtime schemas. Different custom DTO names alone are not different categories.
- Learn more: RFC 9457, including XML; Spring exception media negotiation.
RAPI-ERR-011 - Exception handlers do not expose stack traces
- Severity: HIGH (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. CallingprintStackTraceorgetStackTracemay support server logging and does not prove a trace reaches the HTTP response. The bounded call graph has no return-value data flow. - Recommendation: Keep response diagnostics non-revealing and log details safely, but do not infer a leak from an accessor call. No response capture or new security rule is introduced; the dismissal ID is preserved.
- Learn more: RFC 9457 security considerations.
RAPI-DOC-001 - Consider explicit operation documentation
- Severity: INFO
- Detects: With the optional documentation integration available, a non-hidden handler lacks a recognized explicit Operation annotation.
- Recommendation: Add useful summaries/descriptions where needed. Generated operations, static documents, model readers, and filters may already supply documentation; no annotation does not mean no documented endpoint.
- Learn more: MicroProfile OpenAPI generation; OpenAPI Operation Object.
RAPI-DOC-002 - Consider explicit operation grouping
- Severity: INFO
- Detects: With the optional documentation integration available, no recognized explicit grouping is present. Swagger and MicroProfile Tag annotations, repeatable tag containers, and Operation tag lists count.
- Recommendation: Add meaningful tags if default grouping is insufficient. Tags are optional and generators can group automatically; this does not prove missing groups in the final document.
- Learn more: OpenAPI Operation Object; MicroProfile OpenAPI processing.
RAPI-DOC-003 - Deprecated endpoints signal deprecation to HTTP clients
- Severity: INFO (retained metadata)
- Disposition: Always
SKIPPED; emissions retired. Missing@Operation(deprecated = true)cannot prove missing client signals: SmallRye recognizes Java/Kotlin deprecation, and static documents or filters can also supply it. - Recommendation: Review generated documentation and, where useful, Deprecation/Sunset response headers rather than requiring a redundant annotation. The dismissal ID remains unchanged.
- Learn more: SmallRye 4.2.4 deprecation handling; RFC 9745; RFC 8594.
Complete audit disposition ledger
This is the disposition of the 56-rule accuracy audit in #962, not a validation-run report. It replaces the earlier statement that all 53 then-active rules and severities were retained unchanged. No IDs were added or repurposed. "Retire" below means keep the definition and return SKIPPED.
Sources and version applicability
The audit baseline is Spring Boot 4.1.1 → Spring Framework 7.0.9 (Boot release properties) and Quarkus 3.33.3.1 → SmallRye OpenAPI 4.2.4 (Quarkus BOM), absent application overrides. Spring 7.0.9 mapping/response sources and SmallRye 4.2.4 sources are pinned below. Some resolver/annotation references are the generic Spring 7.0.0 documentation/source baseline; they explain the semantic boundary, not a claim that 7.0.0 is the shipped dependency. Applicability must be pinned by tests against the actual 7.0.9 dependency. This ledger does not claim those tests were run.
Jakarta REST references cover the relevant 3.1/4.0 return, binding, matching, and mapper semantics, not an assertion that every framework implements every optional feature identically. OpenAPI 3.1.1 and MicroProfile OpenAPI 4.0 references distinguish schemas/generated documents from optional annotation enrichment. HTTP RFCs are normative only for the requirements they actually state; resource naming, pagination vocabulary, versioning, and universal Problem Details adoption are not HTTP mandates.
| ID | Disposition and severity | Primary evidence; applicability and inference boundary |
|---|---|---|
| RAPI-MAP-001 | Correct model; MEDIUM retained | Spring mapping, method-condition union. MVC/WebFlux declarations; no inferred mutation. |
| RAPI-MAP-002 | Correct identity/applicability; HIGH retained | Spring mapping, Jakarta matching. Preserve slashes; binding is not JAX-RS dispatch; unknown roots excluded; exact duplicates only. |
| RAPI-MAP-003 | Calibrate HIGH → LOW | RFC 9110 safety. All stacks; mutation-like name is not data flow. |
| RAPI-MAP-004 | Tighten alternatives; LOW retained | Spring mappings. Spring layout preference; interface exemption; every alternative must support shared prefix. |
| RAPI-MAP-005 | Calibrate LOW → INFO | Spring mappings. Optional slash style, not portable normalization or route identity. |
| RAPI-MAP-006 | Restrict required-binding assertion; HIGH retained | Spring required binding, JAX-RS scoped binding. Required explicit Spring alternatives only; optional/map/unknown excluded. |
| RAPI-MAP-007 | Correct normative wording; MEDIUM retained | RFC 9110 GET, HEAD, DELETE. All stacks; interoperability warning, not blanket prohibition. |
| RAPI-MAP-008 | Retire; LOW metadata retained | RFC 5789 literal URI. No {id} does not imply wrong resource cardinality. |
| RAPI-MAP-009 | Restrict parser assertion to Spring; HIGH retained | Spring parser, Jakarta PathParam. Repeated JAX-RS scoped names are not Spring parser failures. |
| RAPI-MAP-010 | Calibrate MEDIUM → INFO | Spring specificity, Jakarta matching. Catch-all is observable; shadowing and response status are not. |
| RAPI-MAP-011 | Qualify threshold; INFO retained | RFC 9110 resources. Three levels is optional style; full depth needs known root. |
| RAPI-NAME-001 | Calibrate LOW → INFO | RFC 9110 resources. English noun heuristic, not protocol grammar. |
| RAPI-NAME-002 | Correct payload facts; INFO retained | RFC 9110 resources. Pluralization optional; no inferred runtime cardinality; binary is not a collection. |
| RAPI-NAME-003 | Calibrate LOW → INFO | RFC 3986 case. Kebab-case optional; legitimate case-sensitive paths remain valid. |
| RAPI-NAME-004 | Retire; LOW metadata retained | Spring 7.0.9 literal-path tests. Explicit dotted mappings do not depend on implicit suffix matching. |
| RAPI-RESP-001 | Narrow; MEDIUM → LOW | RFC 9110 POST, 201. Name-based creation hint; explicit 202/status and dynamic response paths excluded. |
| RAPI-RESP-002 | Narrow; LOW retained | RFC 9110 DELETE, Jakarta returns. Spring default-empty review only; explicit status and JAX-RS void 204 respected. |
| RAPI-RESP-003 | Correct envelope facts; LOW retained | Spring envelopes, entity processor, OpenAPI schema. Nested body envelopes, including HttpEntity, retain payload facts without status authority; generics are not the complete schema. |
| RAPI-RESP-004 | Qualify representation claim; INFO retained | RFC 9110 representations. Scalars valid; structured shape is optional. |
| RAPI-RESP-005 | Narrow no-body inference; LOW retained | Spring returns, Jakarta returns. Nested no-body understood; imperative response paths unknown. |
| RAPI-RESP-006 | Correct status/no-body facts; HIGH retained | RFC 9110 204, Spring entity processor. No-content normative; content-capable declaration is not emitted bytes; HttpEntity has no status. |
| RAPI-RESP-007 | Review status overlap; MEDIUM retained | Spring ResponseStatus, entity processor. Async ResponseEntity counts, HttpEntity does not; reason-aware native dispatch prevents blanket precedence/removal advice. |
| RAPI-RESP-008 | Reword; MEDIUM → INFO | RFC 9110 201. Target URI can identify resource; no runtime header-absence proof. |
| RAPI-RESP-009 | Narrow; LOW → INFO | RFC 9110 HEAD, Spring HEAD suppression. Dedicated-handler efficiency only; shared GET/HEAD, headers, Void/Unit excluded. |
| RAPI-VALID-001 | Correct payload/trigger facts; HIGH → LOW | Spring validation, Jakarta entity validation. Cascade prompt, not proof of unchecked input or executed constraints. |
| RAPI-VALID-002 | Correct reactive request shape; HIGH retained | Spring binding guidance. Entity coupling/over-posting risk, not proven writability of every field. |
| RAPI-VALID-003 | Correct boolean/default/Kotlin handling; MEDIUM retained | MVC resolver, WebFlux resolver. Java numeric null or blank-default conversion failure; false boolean default; uncertain Kotlin defaults skipped. |
| RAPI-VALID-004 | Restrict to aggregate bindings; LOW retained | Spring RequestParam. Unnamed maps only; schemas and allowlists may exist elsewhere. |
| RAPI-VALID-005 | Qualify retry guidance; INFO retained | Idempotency-Key draft. Optional convention; absent argument does not prove missing deduplication. |
| RAPI-DTO-001 | Correct payload/array extraction; HIGH retained | Spring response handling. Persistence exposure risk, no serializer/data-flow proof. |
| RAPI-DTO-002 | Correct inference claim; MEDIUM → LOW | OpenAPI schema. Dynamic objects can have explicit schemas; Jackson 2/3 names supported. |
| RAPI-DTO-004 | Retain INFO | Java records. Public setters are a bounded signal, not a full immutability proof. |
| RAPI-DTO-005 | Correct scope/copy; LOW retained | Java time API. Response DTO fields only; no inferred serialization failure or Boot type replacement. |
| RAPI-PAGE-001 | Narrow collection review; LOW retained | Spring Data paging, response envelopes. No visible paging is not an unbounded query; explicit-media streams stay exempt inside envelopes, Lists do not; binary excluded. |
| RAPI-PAGE-002 | Correct remedy; LOW retained | Spring Data page representation. Spring-only; stable envelope/header alternatives; Slice has no totals guarantee. |
| RAPI-PAGE-003 | Qualify policy; INFO retained | Pagination guidance. Optional consistency; priority-based family detection, not complete mode analysis. |
| RAPI-VER-001 | Correct signals/active stack; INFO retained | Boot MVC, Boot WebFlux. Actual context and supported configuration; no vendor-prefix, negated-condition, or arbitrary-property proof. |
| RAPI-VER-002 | Correct readability claim; LOW retained | Spring mapping, Jakarta media. No consumes does not bypass readers/providers. |
| RAPI-VER-003 | Calibrate LOW → INFO | RFC 9110 Accept. Wildcards valid; broad negotiation is optional review, not disabled negotiation. |
| RAPI-VER-004 | Accept concrete non-JSON formats; INFO retained | RFC 5789, non-JSON example. Missing/broad-format prompt only; parameters do not invalidate a concrete type. |
| RAPI-VER-005 | Correct no-body facts; LOW retained | Spring mapping, Jakarta media. Optional produces consistency; unknown/no-body paths do not prove omission. |
| RAPI-VER-006 | Share corrected version signals; INFO retained | Spring versioning. Positive header and query hints are separate transports; native Spring version condition is not another transport. |
| RAPI-ERR-001 | Correct discovery; MEDIUM → INFO | Reactive advice, Jakarta mappers. Skip incomplete extraction and true aggregate flags with mixed stacks or no JAX-RS mapper evidence; inherited reactive Spring advice can pass; central advice optional. |
| RAPI-ERR-002 | Qualify throws guidance; LOW retained | Java throws. Maintainability only; absent throws does not mean absent failures. |
| RAPI-ERR-003 | Narrow informative comparisons; INFO retained | RFC 9457. Optional Spring convenience-type adoption; custom schemas may comply; unknown/view/void excluded. |
| RAPI-ERR-004 | Correct status observability; MEDIUM retained | Spring exceptions, Jakarta mappers. Dynamic status/error types distinguished, unknown bodies excluded; no invented Quarkus 200 default. |
| RAPI-ERR-005 | Require known fixed non-5xx status; LOW retained | Reactive advice, RFC 9110 5xx. Broad dynamic mappers and sole 500 fallbacks do not prove collapse. |
| RAPI-ERR-006 | Narrow/qualify migration; INFO retained | Spring error responses, RFC 9457. Spring-only known-ancestry exceptions actually declared in throws, without declared class/ancestor/broad coverage; annotation coexistence alone insufficient. |
| RAPI-ERR-007 | Correct header evidence wording; INFO retained | RFC 9110 Retry-After, RFC 6585 429. Optional review of declared status; runtime header absence unknown. |
| RAPI-ERR-008 | Qualify text-error guidance; LOW retained | RFC 9457, Spring exceptions. Body-rendering String only; text is not a protocol violation. |
| RAPI-ERR-009 | Correct exception extraction; MEDIUM retained | Spring ExceptionHandler, Jakarta mappers. Skip incomplete extraction or unresolved imported mapper types; intentional unknown alone is not PARTIAL; declaration union is not scoped resolution. |
| RAPI-ERR-010 | Exclude unknown shapes; LOW retained | RFC 9457, Spring exceptions. Known category differences only under compatible/unspecified media; dynamic Map/Object/Response/raw envelopes/JsonNode excluded; distinct negotiation media alone insufficient. |
| RAPI-ERR-011 | Retire; HIGH metadata retained | RFC 9457 security. Stack-trace accessor calls do not prove response leakage. |
| RAPI-DOC-001 | Reframe as explicit enrichment; INFO retained | MicroProfile generation, OpenAPI operation. No annotation does not prove absent generated/static documentation. |
| RAPI-DOC-002 | Recognize tag forms; INFO retained | OpenAPI operation, MicroProfile processing. Repeatable tags/operation lists count; automatic grouping may exist. |
| RAPI-DOC-003 | Retire; INFO metadata retained | SmallRye 4.2.4 deprecation, RFC 9745. Java/Kotlin annotations or external documents can provide deprecation; header absence unobserved. |
Contract-defect audit (2026)
A second audit re-read every rule against Spring Framework 7.0.9 and Quarkus REST 3.33 sources, pinned the disputed framework behavior with MockMvc and WebTestClient tests in RestApiContractSemanticsTests, and put every addition, removal, and severity change through three independent reviews. It shifted the catalogue from style prompts toward declarations that break requests deterministically. No ID was reused or repurposed.
| ID | Change | Rationale and evidence |
|---|---|---|
| RAPI-VALID-006 | Added, HIGH | Several @RequestBody parameters read one stream; MVC fails every request with 400 (@RequestBody). |
| RAPI-VER-007 | Added, HIGH | Consumes on a bodyless GET/HEAD/DELETE rejects requests without Content-Type with 415 on MVC and WebFlux (consumes matching). |
| RAPI-RESP-010 | Added, MEDIUM | A @ResponseStatus reason discards the returned body on Spring MVC; WebFlux is skipped (MVC reason handling). |
| RAPI-RESP-011 | Added, LOW | An empty Optional read answers 200 with an empty body, not 404 (ResponseEntity.of). |
| RAPI-MAP-002 | Narrowed; HIGH retained | Identical Spring mappings fail startup, so they only appear for inactive alternatives and are no longer reported; partial overlaps fail at request time and still are (handler registration). |
| RAPI-RESP-006 | HIGH → MEDIUM | Servers drop 204 content, so clients see a consistent 204; the defect is a contradictory declaration (RFC 9110 §15.3.5). |
| RAPI-VER-002 | LOW → INFO; method-level advice | Missing consumes is optional explicitness; class-level consumes would trigger RAPI-VER-007. |
| RAPI-VALID-005 | Retired | Name heuristic; deduplication outside handler signatures is invisible (draft history). |
| RAPI-DTO-004 | Retired | Response DTO setters do not affect the HTTP contract; code style. |
| RAPI-ERR-002 | Retired | throws clauses do not affect exception resolution or the HTTP contract; Java style. |
| RAPI-RESP-001, RAPI-RESP-008, RAPI-ERR-007 | Fixed (Quarkus) | Quarkus REST @ResponseStatus(int) and @ResponseHeader are now read, removing a false RAPI-RESP-001 finding on @ResponseStatus(201) (Quarkus REST). |
| RAPI-VER-001, RAPI-VER-006 | Fixed | A leading /v3 segment no longer marks an API handler as documentation; only api-docs/swagger-ui paths do. |
| All emitting rules | Metadata | Rule names now match these headings, and learn-more links point at the specific primary source instead of a generic page. |
Considered and not added: server-side @HttpExchange controller mappings (Spring 6.1+) are usually declared on interfaces and need inherited-mapping resolution the bounded model lacks; an Optional rule for JAX-RS awaits verified Quarkus REST semantics; and counting HttpEntity parameters toward RAPI-VALID-006 was left out to keep that rule's evidence to explicit @RequestBody declarations.
Deliberately deferred checks
- Full native route enumeration, dynamic locator traversal, arbitrary generic substitution, and complete Spring composed/inherited handler resolution need more than this bounded bytecode model.
- Scope/selector/priority-aware matching of every declared exception to runtime resolution is separate from the declaration union used by ERR-009.
- Response-header/content capture and interprocedural data flow are not added to infer status, Location, Retry-After, stack-trace leakage, or actual validation execution.
- Generated/static OpenAPI documents are not parsed; annotation advisories do not certify their completeness.
- Universal caching headers, Vary, Idempotency-Key, Problem Details, URL versions, plural nouns, or one pagination dialect are not mandated from these declarations. RFC 9111 permits caching policies beyond explicit expiration/validator declarations.
- New IDs for additional no-content statuses or additional declaration conflicts are deferred until their evidence can be distinguished from dynamic/null responses and framework suppression.