BootUI
Try it
Setup
Features
Properties
AI agents
Ecosystem
GitHub
Try it
Setup
Features
Properties
AI agents
Ecosystem
GitHub
  • Get started

    • Try the sample app
    • Setup
    • Spring WebFlux
    • Quarkus
    • Activation and safety
    • Non-standard runtimes
    • Troubleshooting
  • Features

    • All features
    • Overview
    • Advisors
    • Runtime
    • Configuration
    • Database
    • Security
    • Services
    • Diagnostics
    • Developer tools
  • Reference

    • Properties
    • Framework support
    • AI agents
    • Command line
    • BootUI family
  • Diagnostic checks

    • Architecture
    • REST API
    • Spring
    • Hibernate
    • Database
    • Security
    • Vulnerabilities
    • Memory
    • Pentesting
    • GraalVM readiness
    • CRaC readiness
    • Quarkus
    • Quarkus security
  • Contributing

    • Repository
    • Specification
    • Implementation plan
    • Quarkus design notes
    • WebFlux design notes
  • Privacy

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-RS Response, and Quarkus RestResponse<T> can select status dynamically. Spring HttpEntity<T> carries headers/body, not status authority. Supported single-valued async forms such as Mono<ResponseEntity<T>>, CompletionStage<ResponseEntity<T>>, and Uni<RestResponse<T>> retain that distinction, as do inverse body-wrapper forms such as ResponseEntity<Mono<T>>. A multi-valued outer Flux<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/Unit in supported single-valued wrappers is no-body; a collection of nullable elements is not. Entity arrays retain their element type. Body-envelope detection includes nested HttpEntity<Object> without giving it status authority. Streaming shape survives supported envelopes such as ResponseEntity<Flux<T>> and RestResponse<Multi<T>>; ResponseEntity<List<T>> remains a collection, not a stream. Binary byte[] is not a pageable resource collection. Direct Spring HttpHeaders is headers-only; HttpEntity<HttpHeaders> and ResponseEntity<HttpHeaders> instead declare a serializable payload. Raw JAX-RS Response has 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 @ResponseHeader names are recorded (Quarkus REST response properties). Like Quarkus itself, both Quarkus annotations are ignored on methods that return Response or RestResponse. An explicit Quarkus status overrides the JAX-RS void → 204 default.
  • Imperative response arguments make the final response unknown. Servlet ServletResponse, OutputStream, Writer, and reactive ServerHttpResponse/ServerWebExchange signatures can write responses directly. A void return 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/Optional and 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-@Validated declarations are recognized conservatively; JAX-RS uses its own entity-validation semantics. Optional Spring primitive boolean is 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's value and exception aliases 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 reactive ResponseEntityExceptionHandler. 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 @RegisterRestClient interfaces, 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.

ScopeHIGHMEDIUMLOWINFOTotal
Stable definitions, including retired emissions88202460
Potentially emitting rules78172153

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.

  1. RAPI-MAP-001MEDIUMUse HTTP-method-specific mappings
  2. RAPI-MAP-002HIGHNo duplicate route mappings
  3. RAPI-MAP-003LOWReview mutation-like names on GET handlers
  4. RAPI-MAP-004LOWPrefer a class-level base path
  5. RAPI-MAP-005INFOReview trailing and doubled slashes
  6. RAPI-MAP-006HIGHRequired Spring path bindings match each path
  7. RAPI-MAP-007MEDIUMReview request entities on GET/HEAD/DELETE
  8. RAPI-MAP-008LOWMutating item methods target an identified resource
  9. RAPI-MAP-009HIGHNo duplicate Spring path-variable tokens
  10. RAPI-MAP-010INFOReview catch-all REST mappings
  11. RAPI-MAP-011INFOReview deeply nested resource paths
  12. RAPI-NAME-001INFOConsider noun-oriented resource paths
  13. RAPI-NAME-002INFOConsider plural collection names
  14. RAPI-NAME-003INFOConsider lowercase kebab-case paths
  15. RAPI-NAME-004LOWNo format-extension suffixes in path segments
  16. RAPI-RESP-001LOWReview the default status of creation-like POST handlers
  17. RAPI-RESP-002LOWReview default empty DELETE responses
  18. RAPI-RESP-003LOWPrefer informative response-envelope body types
  19. RAPI-RESP-004INFOConsider structured read representations
  20. RAPI-RESP-005LOWReview default no-body GET declarations
  21. RAPI-RESP-006MEDIUM204 declarations must not promise content
  22. RAPI-RESP-007MEDIUMReview method-level status and response-envelope overlap
  23. RAPI-RESP-008INFOConsider discoverability for declared 201 responses
  24. RAPI-RESP-009INFOReview dedicated HEAD handler efficiency
  25. RAPI-RESP-010MEDIUMDo not set @ResponseStatus reason on body-returning REST handlers
  26. RAPI-RESP-011LOWMap an empty Optional read to an explicit status
  27. RAPI-VALID-001LOWReview request-payload cascade validation
  28. RAPI-VALID-002HIGHAvoid binding requests directly to JPA entities
  29. RAPI-VALID-003MEDIUMOptional Spring numeric parameters need a nullable/defaulted binding
  30. RAPI-VALID-004LOWReview aggregate query-map contracts
  31. RAPI-VALID-005INFOConsider retry deduplication for creation-like POSTs
  32. RAPI-VALID-006HIGHBind at most one @RequestBody per handler
  33. RAPI-DTO-001HIGHAvoid persistence entities in responses
  34. RAPI-DTO-002LOWPrefer informative response body types
  35. RAPI-DTO-004INFOConsider immutable response DTOs
  36. RAPI-DTO-005LOWConsider java.time in response DTOs
  37. RAPI-PAGE-001LOWReview collection reads without visible pagination
  38. RAPI-PAGE-002LOWPreserve paging metadata for Pageable handlers
  39. RAPI-PAGE-003INFOReview pagination vocabulary differences
  40. RAPI-VER-001INFOReview absent or uneven version signals
  41. RAPI-VER-002INFOConsider explicit consumes declarations
  42. RAPI-VER-003INFOReview wildcard media ranges
  43. RAPI-VER-004INFOState the PATCH document format
  44. RAPI-VER-005LOWReview inconsistent produces declarations
  45. RAPI-VER-006INFOReview mixed versioning strategies
  46. RAPI-VER-007HIGHBodyless handlers must not require a Content-Type
  47. RAPI-ERR-001INFOReview application-wide exception handling declarations
  48. RAPI-ERR-002LOWPrefer informative throws declarations
  49. RAPI-ERR-003INFOConsider Spring ProblemDetail convenience types
  50. RAPI-ERR-004MEDIUMMake error-handler status ownership explicit
  51. RAPI-ERR-005LOWReview broad handlers with fixed non-5xx statuses
  52. RAPI-ERR-006INFOReview mixed Spring error-declaration approaches
  53. RAPI-ERR-007INFOConsider Retry-After for declared 429/503 statuses
  54. RAPI-ERR-008LOWConsider structured error bodies
  55. RAPI-ERR-009MEDIUMReview declared exceptions without a mapping in the model
  56. RAPI-ERR-010LOWReview differing known error contracts
  57. RAPI-ERR-011HIGHException handlers do not expose stack traces
  58. RAPI-DOC-001INFOConsider explicit operation documentation
  59. RAPI-DOC-002INFOConsider explicit operation grouping
  60. RAPI-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"}, or GET,POST /x and GET /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, and patchVersion are 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 @PathVariable is 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 REST PathParam.

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 /configuration already 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, and patch are 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, and news.
  • 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.json and /schema.xml mappings 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 Object body contract, including resolvable single-value async envelopes and nested HttpEntity<Object>. Body-envelope presence does not imply status authority. Plain non-generic JAX-RS Response is 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 @ResponseStatus combined with a status-bearing ResponseEntity, including supported single-value async wrapping. HttpEntity is 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 @ExceptionHandler with a content-capable return whose effective @ResponseStatus (method-level, else class-level) sets a non-blank reason. Spring MVC then calls HttpServletResponse.sendError(status, reason) and returns before return-value handling: the returned body, even a ResponseEntity or ProblemDetail, 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 SKIPPED there. When the adapter cannot tell the active request stack, the rule is SKIPPED as 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 a ResponseEntity, or a ProblemDetail whose detail carries the message. Spring's own documentation calls reason unsuitable 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 as CompletableFuture<Optional<T>>. An Optional inside ResponseEntity is 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 Optional into 404: on MVC and WebFlux the client receives 200 with an empty or JSON null body. Returning a repository findById(...) result directly is the usual cause. An API can intentionally answer 200 with null, 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 @Valid does 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 required retains 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/MultiValueMap aggregate 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 an Idempotency-Key binding 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 @RequestBody parameters. 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 receives null when optional). WebFlux request bodies can be consumed only once. Handlers whose only consumes media type is application/x-www-form-urlencoded are 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 @RequestPart for 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.jackson and Jackson 3's tools.jackson type 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.Date or Calendar. This rule does not inspect request-only DTO fields.
  • Recommendation: Consider Instant, LocalDate, or another appropriate java.time type 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, any api-docs or swagger-ui segment, and similar) are excluded; a leading /v3 is 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 */* or application/*.
  • 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 version condition 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 @RequestPart parameter) but whose effective consumes condition (method-level, which replaces class-level) has no expression matching application/octet-stream. Spring's ConsumesRequestCondition waives its check for requests without a body only when a @RequestBody(required = false) parameter says so; otherwise a missing Content-Type is matched as application/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 without Content-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 Java throws clause influences neither Spring nor Jakarta REST exception resolution nor the HTTP error contract, so throws Exception is 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 @ResponseStatus exception 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 SKIPPED limitation, not automatically PARTIAL; 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 @ExceptionHandler itself.
  • 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. Calling printStackTrace or getStackTrace may 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.

IDDisposition and severityPrimary evidence; applicability and inference boundary
RAPI-MAP-001Correct model; MEDIUM retainedSpring mapping, method-condition union. MVC/WebFlux declarations; no inferred mutation.
RAPI-MAP-002Correct identity/applicability; HIGH retainedSpring mapping, Jakarta matching. Preserve slashes; binding is not JAX-RS dispatch; unknown roots excluded; exact duplicates only.
RAPI-MAP-003Calibrate HIGH → LOWRFC 9110 safety. All stacks; mutation-like name is not data flow.
RAPI-MAP-004Tighten alternatives; LOW retainedSpring mappings. Spring layout preference; interface exemption; every alternative must support shared prefix.
RAPI-MAP-005Calibrate LOW → INFOSpring mappings. Optional slash style, not portable normalization or route identity.
RAPI-MAP-006Restrict required-binding assertion; HIGH retainedSpring required binding, JAX-RS scoped binding. Required explicit Spring alternatives only; optional/map/unknown excluded.
RAPI-MAP-007Correct normative wording; MEDIUM retainedRFC 9110 GET, HEAD, DELETE. All stacks; interoperability warning, not blanket prohibition.
RAPI-MAP-008Retire; LOW metadata retainedRFC 5789 literal URI. No {id} does not imply wrong resource cardinality.
RAPI-MAP-009Restrict parser assertion to Spring; HIGH retainedSpring parser, Jakarta PathParam. Repeated JAX-RS scoped names are not Spring parser failures.
RAPI-MAP-010Calibrate MEDIUM → INFOSpring specificity, Jakarta matching. Catch-all is observable; shadowing and response status are not.
RAPI-MAP-011Qualify threshold; INFO retainedRFC 9110 resources. Three levels is optional style; full depth needs known root.
RAPI-NAME-001Calibrate LOW → INFORFC 9110 resources. English noun heuristic, not protocol grammar.
RAPI-NAME-002Correct payload facts; INFO retainedRFC 9110 resources. Pluralization optional; no inferred runtime cardinality; binary is not a collection.
RAPI-NAME-003Calibrate LOW → INFORFC 3986 case. Kebab-case optional; legitimate case-sensitive paths remain valid.
RAPI-NAME-004Retire; LOW metadata retainedSpring 7.0.9 literal-path tests. Explicit dotted mappings do not depend on implicit suffix matching.
RAPI-RESP-001Narrow; MEDIUM → LOWRFC 9110 POST, 201. Name-based creation hint; explicit 202/status and dynamic response paths excluded.
RAPI-RESP-002Narrow; LOW retainedRFC 9110 DELETE, Jakarta returns. Spring default-empty review only; explicit status and JAX-RS void 204 respected.
RAPI-RESP-003Correct envelope facts; LOW retainedSpring envelopes, entity processor, OpenAPI schema. Nested body envelopes, including HttpEntity, retain payload facts without status authority; generics are not the complete schema.
RAPI-RESP-004Qualify representation claim; INFO retainedRFC 9110 representations. Scalars valid; structured shape is optional.
RAPI-RESP-005Narrow no-body inference; LOW retainedSpring returns, Jakarta returns. Nested no-body understood; imperative response paths unknown.
RAPI-RESP-006Correct status/no-body facts; HIGH retainedRFC 9110 204, Spring entity processor. No-content normative; content-capable declaration is not emitted bytes; HttpEntity has no status.
RAPI-RESP-007Review status overlap; MEDIUM retainedSpring ResponseStatus, entity processor. Async ResponseEntity counts, HttpEntity does not; reason-aware native dispatch prevents blanket precedence/removal advice.
RAPI-RESP-008Reword; MEDIUM → INFORFC 9110 201. Target URI can identify resource; no runtime header-absence proof.
RAPI-RESP-009Narrow; LOW → INFORFC 9110 HEAD, Spring HEAD suppression. Dedicated-handler efficiency only; shared GET/HEAD, headers, Void/Unit excluded.
RAPI-VALID-001Correct payload/trigger facts; HIGH → LOWSpring validation, Jakarta entity validation. Cascade prompt, not proof of unchecked input or executed constraints.
RAPI-VALID-002Correct reactive request shape; HIGH retainedSpring binding guidance. Entity coupling/over-posting risk, not proven writability of every field.
RAPI-VALID-003Correct boolean/default/Kotlin handling; MEDIUM retainedMVC resolver, WebFlux resolver. Java numeric null or blank-default conversion failure; false boolean default; uncertain Kotlin defaults skipped.
RAPI-VALID-004Restrict to aggregate bindings; LOW retainedSpring RequestParam. Unnamed maps only; schemas and allowlists may exist elsewhere.
RAPI-VALID-005Qualify retry guidance; INFO retainedIdempotency-Key draft. Optional convention; absent argument does not prove missing deduplication.
RAPI-DTO-001Correct payload/array extraction; HIGH retainedSpring response handling. Persistence exposure risk, no serializer/data-flow proof.
RAPI-DTO-002Correct inference claim; MEDIUM → LOWOpenAPI schema. Dynamic objects can have explicit schemas; Jackson 2/3 names supported.
RAPI-DTO-004Retain INFOJava records. Public setters are a bounded signal, not a full immutability proof.
RAPI-DTO-005Correct scope/copy; LOW retainedJava time API. Response DTO fields only; no inferred serialization failure or Boot type replacement.
RAPI-PAGE-001Narrow collection review; LOW retainedSpring 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-002Correct remedy; LOW retainedSpring Data page representation. Spring-only; stable envelope/header alternatives; Slice has no totals guarantee.
RAPI-PAGE-003Qualify policy; INFO retainedPagination guidance. Optional consistency; priority-based family detection, not complete mode analysis.
RAPI-VER-001Correct signals/active stack; INFO retainedBoot MVC, Boot WebFlux. Actual context and supported configuration; no vendor-prefix, negated-condition, or arbitrary-property proof.
RAPI-VER-002Correct readability claim; LOW retainedSpring mapping, Jakarta media. No consumes does not bypass readers/providers.
RAPI-VER-003Calibrate LOW → INFORFC 9110 Accept. Wildcards valid; broad negotiation is optional review, not disabled negotiation.
RAPI-VER-004Accept concrete non-JSON formats; INFO retainedRFC 5789, non-JSON example. Missing/broad-format prompt only; parameters do not invalidate a concrete type.
RAPI-VER-005Correct no-body facts; LOW retainedSpring mapping, Jakarta media. Optional produces consistency; unknown/no-body paths do not prove omission.
RAPI-VER-006Share corrected version signals; INFO retainedSpring versioning. Positive header and query hints are separate transports; native Spring version condition is not another transport.
RAPI-ERR-001Correct discovery; MEDIUM → INFOReactive 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-002Qualify throws guidance; LOW retainedJava throws. Maintainability only; absent throws does not mean absent failures.
RAPI-ERR-003Narrow informative comparisons; INFO retainedRFC 9457. Optional Spring convenience-type adoption; custom schemas may comply; unknown/view/void excluded.
RAPI-ERR-004Correct status observability; MEDIUM retainedSpring exceptions, Jakarta mappers. Dynamic status/error types distinguished, unknown bodies excluded; no invented Quarkus 200 default.
RAPI-ERR-005Require known fixed non-5xx status; LOW retainedReactive advice, RFC 9110 5xx. Broad dynamic mappers and sole 500 fallbacks do not prove collapse.
RAPI-ERR-006Narrow/qualify migration; INFO retainedSpring 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-007Correct header evidence wording; INFO retainedRFC 9110 Retry-After, RFC 6585 429. Optional review of declared status; runtime header absence unknown.
RAPI-ERR-008Qualify text-error guidance; LOW retainedRFC 9457, Spring exceptions. Body-rendering String only; text is not a protocol violation.
RAPI-ERR-009Correct exception extraction; MEDIUM retainedSpring 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-010Exclude unknown shapes; LOW retainedRFC 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-011Retire; HIGH metadata retainedRFC 9457 security. Stack-trace accessor calls do not prove response leakage.
RAPI-DOC-001Reframe as explicit enrichment; INFO retainedMicroProfile generation, OpenAPI operation. No annotation does not prove absent generated/static documentation.
RAPI-DOC-002Recognize tag forms; INFO retainedOpenAPI operation, MicroProfile processing. Repeatable tags/operation lists count; automatic grouping may exist.
RAPI-DOC-003Retire; INFO metadata retainedSmallRye 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.

IDChangeRationale and evidence
RAPI-VALID-006Added, HIGHSeveral @RequestBody parameters read one stream; MVC fails every request with 400 (@RequestBody).
RAPI-VER-007Added, HIGHConsumes on a bodyless GET/HEAD/DELETE rejects requests without Content-Type with 415 on MVC and WebFlux (consumes matching).
RAPI-RESP-010Added, MEDIUMA @ResponseStatus reason discards the returned body on Spring MVC; WebFlux is skipped (MVC reason handling).
RAPI-RESP-011Added, LOWAn empty Optional read answers 200 with an empty body, not 404 (ResponseEntity.of).
RAPI-MAP-002Narrowed; HIGH retainedIdentical 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-006HIGH → MEDIUMServers drop 204 content, so clients see a consistent 204; the defect is a contradictory declaration (RFC 9110 §15.3.5).
RAPI-VER-002LOW → INFO; method-level adviceMissing consumes is optional explicitness; class-level consumes would trigger RAPI-VER-007.
RAPI-VALID-005RetiredName heuristic; deduplication outside handler signatures is invisible (draft history).
RAPI-DTO-004RetiredResponse DTO setters do not affect the HTTP contract; code style.
RAPI-ERR-002Retiredthrows clauses do not affect exception resolution or the HTTP contract; Java style.
RAPI-RESP-001, RAPI-RESP-008, RAPI-ERR-007Fixed (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-006FixedA leading /v3 segment no longer marks an API handler as documentation; only api-docs/swagger-ui paths do.
All emitting rulesMetadataRule 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.
Prev
Architecture
Next
Spring