BootUI
Try it
Setup
Features
Properties
AI agents
Ecosystem
GitHub
Try it
Setup
Features
Properties
AI agents
Ecosystem
GitHub
  • Project documentation

    • Try the sample app
    • Setup
    • BootUI feature details
    • BootUI properties
    • AI agents
    • The BootUI family
    • Repository and documentation
    • BootUI Specification
    • BootUI Implementation Plan
    • BootUI on Quarkus — design & strategy
    • BootUI on Spring WebFlux — support status
  • Diagnostic checks

    • Architecture
    • REST API
    • Spring Advisor
    • Hibernate Advisor
    • Database
    • Spring Security Advisor
    • Memory advisor
    • Pentesting
    • GraalVM readiness
    • CRaC readiness
    • Quarkus Application Advisor
    • Quarkus Security Advisor
  • Additional docs

    • JVM Tuning Advisor Model

REST API checks

The REST API panel runs a fixed, zero-config ruleset against the host application's own web layer — Spring MVC (@RestController / @Controller handler methods) or, on Quarkus, JAX-RS/RESTEasy Reactive (@Path resource methods). This page lists every rule that ships with BootUI today, what it inspects, when it fires, and what to do about it.

Each rule is a small class registered in RestApiRuleRegistry and implemented in RestApiRules.java, both in the framework-neutral bootui-engine module. The same 56 rule definitions are evaluated on Spring Boot and Quarkus from one bounded handler model; rules whose required framework fact is not observable are skipped rather than guessed. The list intentionally stays compact and reviewable; adding a new rule means adding one focused class plus a registry entry.

What BootUI does

The scanner detects the host application's base package(s) — from the @SpringBootApplication configuration via AutoConfigurationPackages on Spring, or from a build-time Jandex index on Quarkus — imports the compiled .class files from those packages with ArchUnit's ClassFileImporter, derives a read-only handler model (standard and custom JAX-RS HTTP method(s), path(s), parameters and their annotations, return type, produces/consumes, validation flags, declared throws) once, and evaluates every registered rule against that model. Importing is bounded to the application's own base package(s) — never the entire classpath — and runs only on demand when the scan action is invoked, caching the last report in the controller. A handful of rules reason about Spring-specific types (ProblemDetail/ErrorResponse convenience types or Spring Data Pageable); on Quarkus their evaluation is SKIPPED rather than misfiring. The public report intentionally contains findings only, so PASS, SKIPPED, and ERROR outcomes do not appear as result rows. RFC 9457 itself is framework-neutral; the limitation is that the bounded model cannot reliably prove that an arbitrary JAX-RS payload implements its schema — see RAPI-PAGE-002, RAPI-ERR-003, and RAPI-ERR-006 below.

The handler model unwraps common async/reactive return-type wrappers before classifying the response body, so the body-shape rules below (RAPI-DTO-001/002/004/005, RAPI-RESP-004, RAPI-PAGE-001/002, RAPI-NAME-002) see the real payload type on both frameworks: Spring's Mono, CompletableFuture, CompletionStage, DeferredResult, and WebAsyncTask, and — on Quarkus/JAX-RS — SmallRye Mutiny's Uni/Multi (the officially-promoted reactive return types for Quarkus REST resources) and RESTEasy Reactive's typed RestResponse<T>. Plain jakarta.ws.rs.core.Response is intentionally left unwrapped: it is non-generic, and Quarkus itself cannot determine its contained type at build time.

When BootUI is installed through bootui-spring-boot-starter, ArchUnit is included transitively so the panel works without an extra application dependency. The panel is available only when:

  • ArchUnit is on the classpath, and
  • a base package is resolvable from the running application.

Every rule catches RuntimeException and LinkageError so one unresolvable class never aborts the scan. Non-violation outcomes are omitted from the public finding list, and the scanner degrades to a stable "scanned, nothing to analyze" report when no controllers can be imported.

What BootUI does not do

  • It does not check security concerns (CORS, authentication, authorization) — those remain owned by the Security panel.
  • It does not modify, compile, or instrument application code; it reads already-compiled bytecode.
  • It excludes MicroProfile @RegisterRestClient interfaces: they are outbound client proxies, not inbound JAX-RS resources.
  • It does not infer statuses, Location, Retry-After, or problem-details payloads from imperative Response/ResponseEntity builder chains. Rules only act on bounded, directly observable handler facts.
  • It treats unresolved ${...} property placeholders and #{...} SpEL path segments as opaque rather than guessing whether their resolved values follow a naming convention.
  • It is not a replacement for an API design review or contract testing. The heuristics are project-agnostic review prompts, not verdicts.

Severities

Findings are ranked in the scanner's severity order:

  • CRITICAL — supported by the scanner, but no active REST API rule currently assigns it.
  • HIGH
  • MEDIUM
  • LOW
  • INFO

The catalogue below ships 56 rules across 8 categories (9 HIGH, 11 MEDIUM, 21 LOW, 15 INFO; no active CRITICAL rules). The RAPI-DOC-* documentation rules only run when OpenAPI annotations are present on the host classpath — Swagger/springdoc-openapi (io.swagger.v3.oas.annotations) on Spring, or MicroProfile OpenAPI (org.eclipse.microprofile.openapi.annotations) on Quarkus through quarkus-smallrye-openapi. The shared model recognizes both Swagger and MicroProfile annotation forms when that platform's documentation integration is available; otherwise the rules are skipped without emitting a finding.

The advisor score applies the shared severity penalty to every concrete finding, not just once per violated rule. Dismissed rules remove all of their findings from the score.


Routing & HTTP method mapping

RAPI-MAP-001 - Use HTTP-method-specific mappings

  • Severity: MEDIUM
  • Detects: Handlers mapped with @RequestMapping but no HTTP method (neither on the method itself nor inherited from a class-level @RequestMapping method attribute) match every verb, which hides intent and can expose state-changing operations over GET.
  • Recommendation: Replace @RequestMapping without a method with @GetMapping/@PostMapping/@PutMapping/@DeleteMapping/@PatchMapping (or set the method attribute at the method or class level).
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-002 - No duplicate route mappings

  • Severity: HIGH
  • Detects: Two handlers with the same HTTP method, path, consumes/produces, params, headers, and version lead to ambiguous mapping exceptions at startup or unpredictable dispatch.
  • Recommendation: Ensure each (HTTP method, path, consumes/produces/params/headers/version) combination is handled by exactly one method.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-003 - State-changing handlers are not mapped to GET

  • Severity: HIGH
  • Detects: GET must be safe and idempotent. A create/update/delete/save-style handler mapped to GET can be triggered by crawlers, prefetching, or caching. Ambiguous HTTP-verb prefixes are intentionally excluded, so names such as postProcess, putAside, and patchVersion do not create false positives.
  • Recommendation: Map state-changing operations to POST/PUT/PATCH/DELETE instead of GET.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-MAP-004 - Prefer a class-level base path

  • Severity: LOW
  • Detects: Controllers that repeat the same leading path segment on every method but declare no type-level @RequestMapping duplicate routing information.
  • Recommendation: Hoist the shared prefix into a class-level @RequestMapping and keep method paths relative.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-005 - Consistent path style (no trailing slash)

  • Severity: LOW
  • Detects: Trailing slashes and doubled slashes in class-level or method mapping paths create inconsistent URLs; trailing-slash matching is also disabled by default in Spring 6+.
  • Recommendation: Declare mapping paths without trailing slashes and without empty segments.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-006 - @PathVariable names match a path token

  • Severity: HIGH
  • Detects: A @PathVariable whose explicit name has no matching {token} in the mapping path fails at runtime with a missing-path-variable error.
  • Recommendation: Make each @PathVariable name match a {token} in the mapping path (or correct the path template).
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-007 - No @RequestBody on GET/HEAD/DELETE

  • Severity: MEDIUM
  • Detects: A @RequestBody on a GET, HEAD, or DELETE handler relies on a request body that proxies, caches, and HTTP clients may strip, so the payload is unreliable.
  • Recommendation: Move the payload to query/path parameters, or use POST/PUT/PATCH when a request body is required.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-MAP-008 - Mutating item methods target an identified resource

  • Severity: LOW
  • Detects: PUT/PATCH/DELETE handlers with a path but no {id} (or other path variable), excluding explicit bulk/batch/all and singleton/current-resource endpoints, mutate a collection URI rather than a specific resource.
  • Recommendation: Add a path variable that identifies the resource (e.g. /orders/{id}); for intentional collection-wide mutations, name the endpoint explicitly (e.g. /orders/bulk).
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-MAP-009 - No duplicate path-variable tokens in one template

  • Severity: HIGH
  • Detects: A path template with the same {token} name in two positions (e.g. /users/{id}/orders/{id}) is ambiguous — Spring can only bind one value to the parameter, and the mapping is broken by construction.
  • Recommendation: Use distinct token names for each path variable (e.g. /users/{userId}/orders/{orderId}).
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-010 - No catch-all wildcard patterns on REST handlers

  • Severity: MEDIUM
  • Detects: A /** or {path} catch-all on a REST handler silently shadows sibling routes and swallows typos as 200 OK responses, masking 404s and making API discovery unreliable. On JAX-RS/Quarkus, also flags the equivalent regex catch-all path-parameter template (e.g. {path:.} or {path:.+}); a constrained template such as {id:[0-9]+} is not flagged.
  • Recommendation: Map each endpoint explicitly; use a dedicated wildcard handler only for truly generic forwarding outside the REST API surface.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-MAP-011 - Resource nesting depth should not exceed 3 levels

  • Severity: INFO
  • Detects: Path templates with more than 3 collection/{id} pairs (e.g. /a/{aId}/b/{bId}/c/{cId}/d/{dId}) are hard to read, often indicate a missing intermediate resource, and produce unwieldy URLs.
  • Recommendation: Flatten deep nesting by exposing a top-level resource or limiting path templates to at most 3 collection/{id} pairs.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

Naming & resource design

RAPI-NAME-001 - Resource paths are nouns, not verbs

  • Severity: LOW
  • Detects: Verb-based path segments such as /getUser or /createOrder duplicate the HTTP method and break the resource-oriented REST model. Ambiguous English words that are also HTTP method names (post, put, patch) are only flagged when used in camelCase (e.g. /postMessage), not as standalone segments where they may be legitimate nouns (e.g. /blog/post/{id}).
  • Recommendation: Model resources as nouns (/users, /orders) and express the action with the HTTP method.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-NAME-002 - Collections use plural nouns

  • Severity: INFO
  • Detects: Endpoints returning a collection, Page, or Slice but addressed with a singular noun read inconsistently (/user vs /users). Uncountable or collective nouns (history, inventory, staff, info, search, news, status, etc.) are not flagged.
  • Recommendation: Use plural nouns for collection resources and keep singular forms for single-item paths.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-NAME-003 - Path segments are kebab-case/lowercase

  • Severity: LOW
  • Detects: camelCase, snake_case, or upper-case path segments produce inconsistent, case-sensitive URLs.
  • Recommendation: Use lower-case kebab-case path segments (/order-items, not /orderItems or /order_items).
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-NAME-004 - No format-extension suffixes in path segments

  • Severity: LOW
  • Detects: Path segments ending in .json, .xml, or similar format suffixes rely on suffix content negotiation, which was removed in Spring Framework 6 and is not supported in Spring Framework 7.
  • Recommendation: Use the Accept header for content negotiation and remove format-extension suffixes from paths.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

Status codes & responses

RAPI-RESP-001 - Creation endpoints return 201 Created

  • Severity: MEDIUM
  • Detects: A POST that creates a resource but returns the default 200 OK hides the created status and (usually) the Location of the new resource.
  • Recommendation: Prefer ResponseEntity.created(uri) so the response carries both 201 and the Location header; use @ResponseStatus(HttpStatus.CREATED) only when the Location is set another way.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html#section-15.3.2

RAPI-RESP-002 - Void DELETE returns 204 No Content

  • Severity: LOW
  • Detects: A DELETE handler returning void but defaulting to 200 OK sends an empty 200 instead of the more precise 204 No Content. On JAX-RS/Quarkus this never fires: a void resource method already defaults to 204 per the Jakarta REST spec, unlike Spring MVC.
  • Recommendation: Annotate void DELETE handlers with @ResponseStatus(HttpStatus.NO_CONTENT) or return ResponseEntity.noContent().
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-RESP-003 - No untyped ResponseEntity body

  • Severity: LOW
  • Detects: ResponseEntity<?> or ResponseEntity<Object> erases the response contract, so clients and OpenAPI tooling cannot infer the body type.
  • Recommendation: Parameterize ResponseEntity with the concrete DTO type returned by the handler.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-RESP-004 - Read endpoints return a representation

  • Severity: INFO
  • Detects: A GET returning a bare String or primitive without explicitly producing text/* exposes a value without a stable, evolvable representation.
  • Recommendation: Return a DTO/record representation from read endpoints instead of a raw String or primitive.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-RESP-005 - GET endpoints return content

  • Severity: LOW
  • Detects: A GET handler that returns void responds with an empty 200 OK and no representation, which is rarely the intent for a read endpoint. On JAX-RS/Quarkus this never fires: a void resource method already answers 204 No Content per the Jakarta REST spec, so there is no "silent 200 OK" to flag there.
  • Recommendation: Return the resource representation from GET handlers (or use a more precise status when no body is expected).
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-RESP-006 - 204 No Content responses carry no body

  • Severity: HIGH
  • Detects: A handler annotated @ResponseStatus(NO_CONTENT) that still returns a body is contradictory: 204 forbids a response body and clients/proxies may drop or reject it.
  • Recommendation: Return void (or ResponseEntity) for 204 responses, or use 200 OK when a body is required.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

RAPI-RESP-007 - @ResponseStatus is not combined with ResponseEntity

  • Severity: MEDIUM
  • Detects: When a handler returns ResponseEntity, its status wins and a method-level @ResponseStatus is silently ignored, so the declared status is misleading. A class-level @ResponseStatus is not flagged here since it can serve as a default for non-ResponseEntity methods.
  • Recommendation: Set the status through ResponseEntity (e.g. ResponseEntity.status(...)) and drop the redundant method-level @ResponseStatus.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-RESP-008 - Created responses expose a Location

  • Severity: MEDIUM
  • Detects: A handler annotated @ResponseStatus(CREATED) that returns a plain body (not ResponseEntity and with no response-mutating MVC/WebFlux argument) has no way to set the Location header of the newly created resource.
  • Recommendation: Return ResponseEntity.created(uri).body(...) so the 201 response also carries the Location of the newly created resource, or set it through an explicit response argument.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html#section-15.3.2

RAPI-RESP-009 - HEAD handlers do not return response bodies

  • Severity: LOW
  • Detects: A dedicated HEAD handler returns a body even though RFC 9110 requires a HEAD response to omit message content. This catches both Spring RequestMethod.HEAD handlers and JAX-RS @HEAD resource methods.
  • Recommendation: Return void or a headers-only response from dedicated HEAD handlers; let the framework derive HEAD from GET when no distinct metadata calculation is needed.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html

Input validation & binding

RAPI-VALID-001 - @RequestBody is validated

  • Severity: HIGH
  • Detects: A complex @RequestBody parameter without @Valid/@Validated is bound without bean-validation, so malformed payloads reach the business logic unchecked. On JAX-RS, only a parameter-level @Valid counts — a class-level @Validated is not a meaningful JAX-RS idiom and is no longer treated as a substitute, keeping both framework paths symmetric.
  • Recommendation: Annotate @RequestBody parameters with @Valid (or @Validated) and declare constraints on the DTO.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html

RAPI-VALID-002 - No mass-assignment via JPA entities

  • Severity: HIGH
  • Detects: Binding a request body directly to a JPA @Entity lets clients set any persistent field (mass-assignment / over-posting), including ids and relationships.
  • Recommendation: Bind requests to a dedicated request DTO and map explicitly to the entity.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html

RAPI-VALID-003 - Optional @RequestParam is not a primitive

  • Severity: MEDIUM
  • Detects: A primitive @RequestParam with required=false and no defaultValue throws 500 (IllegalStateException) when the parameter is omitted, because null cannot be unboxed.
  • Recommendation: Use the boxed wrapper type (e.g. Integer) or provide a defaultValue for optional primitive query parameters.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html

RAPI-VALID-004 - Avoid @RequestParam Map/MultiValueMap on public endpoints

  • Severity: LOW
  • Detects: @RequestParam Map<String,?> or MultiValueMap binds every query parameter into an untyped map, creating an unbounded, undocumented input contract that is invisible to OpenAPI tooling and cannot be validated with bean-validation constraints.
  • Recommendation: Declare each accepted query parameter explicitly with a typed @RequestParam so the contract is self-documenting and validatable.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller/ann-validation.html

RAPI-VALID-005 - Consider an Idempotency-Key header on creation endpoints

  • Severity: INFO
  • Detects: A POST creation endpoint (method name starting with create/add/save/insert/register/new) has no client-supplied Idempotency-Key header (@RequestHeader on Spring, @HeaderParam/@RestHeader on JAX-RS), so a client cannot safely retry the request after a network failure without risking a duplicate resource. draft-ietf-httpapi-idempotency-key-header-07 expired in April 2026 without becoming an RFC, but the header remains useful de-facto guidance implemented by major payment APIs.
  • Recommendation: Worth a design review: accept an Idempotency-Key header and de-duplicate retried requests by that key for non-idempotent creation endpoints.
  • Learn more: https://datatracker.ietf.org/doc/draft-ietf-httpapi-idempotency-key-header/

DTO & payload contracts

RAPI-DTO-001 - Don't expose JPA entities in responses

  • Severity: HIGH
  • Detects: Returning a JPA @Entity couples the API to the persistence model and can leak lazy associations or internal fields and trigger serialization-time queries.
  • Recommendation: Return a response DTO/record and map from the entity in the service or controller.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-DTO-002 - No untyped response bodies

  • Severity: MEDIUM
  • Detects: Returning Map, Object, or JsonNode as the body produces an undocumented, untyped contract that clients and OpenAPI tooling cannot model. Recognises both Jackson 2's com.fasterxml.jackson.databind.JsonNode (Quarkus) and Jackson 3's tools.jackson.databind.JsonNode (Spring Boot 4).
  • Recommendation: Return a typed DTO/record instead of Map/Object/JsonNode.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-DTO-004 - Response DTOs are immutable

  • Severity: INFO
  • Detects: Response payload types that expose public setters are mutable, which makes them easy to mutate accidentally and harder to reason about.
  • Recommendation: Prefer Java records or otherwise immutable response DTOs without public setters.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-DTO-005 - Response/request DTOs should use java.time, not java.util.Date/Calendar

  • Severity: LOW
  • Detects: Fields typed java.util.Date or java.util.Calendar have ambiguous timezone semantics and serialise inconsistently across Jackson versions; Spring Boot 4 defaults to java.time.
  • Recommendation: Replace java.util.Date/Calendar fields with java.time equivalents (Instant, LocalDate, ZonedDateTime, etc.).
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

Pagination & collections

RAPI-PAGE-001 - Collection reads are paginated

  • Severity: LOW
  • Detects: A GET returning a Collection with no Pageable parameter and no manual pagination parameter (page, size, limit, offset, cursor, after, before, etc.) loads and serializes the entire result set, which does not scale.
  • Recommendation: Accept a Pageable parameter (or explicit page/size/cursor) and return a bounded result.
  • Learn more: https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html

RAPI-PAGE-002 - Pageable handlers return a paged type

  • Severity: LOW
  • Detects: A Spring handler that accepts Spring Data Pageable but returns a raw List/array discards the paging metadata (total elements, total pages) that Page or Slice would carry. On JAX-RS/Quarkus the rule reports SKIPPED: the current model sees explicit page/size/cursor parameters but has no reliable framework-neutral metadata proving which response type is the application's pagination envelope.
  • Recommendation: On Spring, return Page or Slice when the handler accepts Pageable. On JAX-RS, document and return an explicit pagination envelope.
  • Learn more: https://docs.spring.io/spring-data/commons/reference/repositories/core-extensions.html

RAPI-PAGE-003 - Consistent pagination parameter vocabulary across handlers

  • Severity: INFO
  • Detects: Handlers in the same application key pagination on different vocabularies (some use page/size, others offset/limit, others cursor/after/before), producing an inconsistent contract that clients must handle specially depending on which endpoint they call.
  • Recommendation: Settle on a single pagination parameter vocabulary (page/size, offset/limit, or cursor/after/before) and apply it uniformly across all paginated endpoints.
  • Learn more: https://opensource.zalando.com/restful-api-guidelines/#pagination

Versioning & content negotiation

RAPI-VER-001 - API uses a consistent versioning strategy

  • Severity: INFO
  • Detects: No version signal (no /vN path segment, version header/param, versioned media type, or Spring Framework 7 version attribute) was found, or only some handlers are versioned, which makes breaking changes hard to roll out consistently. Recognises JAX-RS's @HeaderParam/@QueryParam and Quarkus's @RestHeader/@RestQuery for header/query-param versioning, matching Spring's @RequestHeader/@RequestParam. On Spring MVC, also passes when runtime-wide versioning is configured via spring.mvc.apiversion.* properties (default, supported, or any use.* key), since that strategy is not expressible per-handler in bytecode.
  • Recommendation: Adopt one versioning strategy (path, header/param, media-type, or Spring Framework 7 version attribute) and apply it across all API endpoints before the API is consumed externally.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc-versioning.html

RAPI-VER-002 - Mutating endpoints declare a consumes media type

  • Severity: LOW
  • Detects: POST/PUT/PATCH handlers that accept a @RequestBody but declare no consumes media type accept any content type, which weakens content negotiation and input validation.
  • Recommendation: Declare consumes (e.g. application/json) on mutating endpoints that accept a request body.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-VER-003 - No wildcard media types

  • Severity: LOW
  • Detects: Declaring produces/consumes with a wildcard (/ or application/*) disables meaningful content negotiation and defeats the purpose of declaring media types.
  • Recommendation: Use concrete media types (e.g. application/json) instead of wildcard media types.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-VER-004 - PATCH declares a patch media type

  • Severity: INFO
  • Detects: A PATCH handler that declares no consumes media type at all, or declares a non-JSON concrete type, does not signal which patch document format it expects. Plain application/json is accepted without flagging (the common, legitimate partial-update pattern used by Google/Microsoft-style APIs with an update_mask/updateMask field) since RFC 5789 does not mandate the RFC 7396/6902 media types specifically.
  • Recommendation: Declare an explicit consumes media type on PATCH handlers - application/merge-patch+json (RFC 7396), application/json-patch+json (RFC 6902), or plain application/json paired with a documented partial-update convention.
  • Learn more: https://www.rfc-editor.org/rfc/rfc5789.html

RAPI-VER-005 - Response-producing endpoints declare produces consistently

  • Severity: LOW
  • Detects: Within a controller that declares produces media types on some response handlers, other body-returning handlers that omit produces create an inconsistent content contract.
  • Recommendation: Declare produces (e.g. application/json) consistently on the response-producing handlers of a controller.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-controller.html

RAPI-VER-006 - Consistent versioning strategy across handlers

  • Severity: INFO
  • Detects: Handlers in the same application use different API versioning strategies (e.g. some use /vN/ path segments, others use versioned media types or version headers/params), producing an inconsistent contract that clients must handle specially. Header/query-param signals are recognised on JAX-RS handlers too (see RAPI-VER-001).
  • Recommendation: Settle on a single versioning strategy (path, header/param, media-type, or Spring Framework 7 version attribute) and apply it uniformly.
  • Learn more: https://docs.spring.io/spring-framework/reference/web/webmvc-versioning.html

Error handling & documentation

RAPI-ERR-001 - Centralized exception handling exists

  • Severity: MEDIUM
  • Detects: Controllers are present but no @RestControllerAdvice/@ControllerAdvice with @ExceptionHandler methods (or a ResponseEntityExceptionHandler subclass) was found, so error responses are likely ad-hoc and inconsistent. On JAX-RS/Quarkus, a classic @Provider-annotated ExceptionMapper<X> or a RESTEasy Reactive @ServerExceptionMapper-annotated method both count as centralized handling.
  • Recommendation: Add a @RestControllerAdvice with @ExceptionHandler methods (or extend ResponseEntityExceptionHandler) to produce consistent error responses.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-002 - No broad throws on handlers

  • Severity: LOW
  • Detects: Handlers declaring throws Exception or Throwable obscure the real failure modes and discourage targeted exception handling.
  • Recommendation: Throw specific exceptions and map them with @ExceptionHandler instead of declaring throws Exception/Throwable.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-003 - Prefer RFC 9457 ProblemDetail

  • Severity: INFO
  • Detects: @ExceptionHandler methods that model errors as ad-hoc maps/strings instead of Spring's ProblemDetail convenience type produce non-standard error payloads. On JAX-RS/Quarkus the rule reports SKIPPED: RFC 9457 is framework-neutral, but the current model cannot reliably prove that an arbitrary JAX-RS DTO implements the problem-details schema.
  • Recommendation: Return ProblemDetail (or ErrorResponse) from @ExceptionHandler methods for RFC 9457 compliant errors.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-004 - Exception handlers set an explicit error status

  • Severity: MEDIUM
  • Detects: An @ExceptionHandler that renders a non-ProblemDetail body but neither returns ResponseEntity, declares @ResponseStatus, nor accepts a servlet response argument falls back to 200 OK, masking the failure from clients. JAX-RS's classic ExceptionMapper<X> and RESTEasy Reactive's @ServerExceptionMapper methods are now modeled the same way as Spring @ExceptionHandler methods, so this rule evaluates real JAX-RS exception-handling quality instead of vacuously passing.
  • Recommendation: Return ResponseEntity/ProblemDetail or add @ResponseStatus so the handler responds with an error status.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-005 - Broad @ExceptionHandler should not collapse all errors to one status

  • Severity: LOW
  • Detects: @ExceptionHandler(Exception.class) or Throwable is the only exception handler declared by its advice/mapper class (so that class collapses every error it handles to one status), or it maps to a fixed non-5xx status. The comparison is per declaring class, not application-wide. A broad catch-all mapped to a 5xx status (RFC 9110 §15.6.1) that coexists with specific handlers in the same class is a deliberate last-resort fallback and is not flagged.
  • Recommendation: Catch specific exception types and map each to its appropriate status (e.g. 400, 404, 409), and keep any Exception/Throwable catch-all as a last-resort fallback mapped to a 5xx status.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-006 - Prefer ErrorResponseException over @ResponseStatus on exceptions

  • Severity: INFO
  • Detects: The project uses Spring's ProblemDetail (RFC 9457) support for error responses but some application exception classes are annotated with @ResponseStatus instead, mixing Spring error-handling approaches. On JAX-RS/Quarkus the rule reports SKIPPED because both inspected types are Spring-specific; RFC 9457 itself remains framework-neutral.
  • Recommendation: Replace @ResponseStatus on exception classes with ErrorResponseException (or a subclass) so all errors consistently produce RFC 9457 ProblemDetail responses.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-007 - 429/503 responses should advertise Retry-After

  • Severity: INFO
  • Detects: A handler or exception handler maps to 429 Too Many Requests or 503 Service Unavailable with no statically-visible Retry-After header, leaving clients to guess when it is safe to retry. This is a weak, advisory signal: an imperatively-set header cannot be detected by static analysis, so treat this as a reminder rather than a confirmed defect.
  • Recommendation: Set a Retry-After header (RFC 9110 §10.2.3) alongside 429 (RFC 6585 §4) or 503 responses so clients know when to retry. For quota visibility, RateLimit and RateLimit-Policy are complementary fields from the active draft-ietf-httpapi-ratelimit-headers; as of July 2026 they are not an RFC.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html#section-10.2.3

RAPI-ERR-008 - Exception handlers return structured errors

  • Severity: LOW
  • Detects: A Spring @ExceptionHandler or Quarkus REST @ServerExceptionMapper returns a raw String, exposing an unstructured error contract with no stable fields for type, status, detail, or correlation metadata.
  • Recommendation: Return a typed error DTO or an RFC 9457 problem-details representation instead of a raw String.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-009 - Declared exceptions have a declared handler

  • Severity: MEDIUM
  • Detects: An endpoint declares an application exception in its throws clause that no @ExceptionHandler, exception mapper, or @ResponseStatus-annotated exception maps (supertypes count as coverage), so the failure falls through to the framework's default error response instead of the application's error contract. The rule is silent when the application declares no exception handlers at all, because RAPI-ERR-001 already reports that, and it never infers a missing mapping from the absence of observed failures.
  • Recommendation: Map the exception with an @ExceptionHandler/exception mapper, or annotate it with @ResponseStatus, so its HTTP status and body are part of the declared contract.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-010 - Error responses share one contract

  • Severity: LOW
  • Detects: The declared exception handlers disagree on the error contract — either they return more than one response-body shape (problem details, a custom object, a raw String, an untyped map) or they declare more than one error media type — so clients must parse several error formats from the same API. Only body-rendering handlers are compared, and no response content is read or reported.
  • Recommendation: Return one error representation — ideally an RFC 9457 problem-details document — from every exception handler, and declare the same error media type.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-ERR-011 - Exception handlers do not expose stack traces

  • Severity: HIGH
  • Detects: A declared exception handler calls a stack-trace accessor (printStackTrace, getStackTrace, fillInStackTrace, or a getStackFrames/getFullStackTrace helper), which commonly leaks internal class names, file paths, and framework internals into an error response. The evidence is the handler's own declared call graph; the report names only the handler and never includes a captured message, response body, or stack trace.
  • Recommendation: Log the failure with a correlation identifier through the logging framework and return only a stable, non-revealing error representation to the client.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9457.html

RAPI-DOC-001 - Endpoints are documented

  • Severity: INFO
  • Detects: OpenAPI annotations (Swagger's @Operation/@Tag, honored by springdoc-openapi on Spring, or the identical MicroProfile OpenAPI @Operation/@Tag, honored by SmallRye OpenAPI on Quarkus) are present on the classpath but some handlers have no @Operation, so the generated documentation is incomplete.
  • Recommendation: Add @Operation (summary/description) to handler methods to document the API.
  • Learn more: https://springdoc.org/

RAPI-DOC-002 - Controllers are grouped/tagged

  • Severity: INFO
  • Detects: OpenAPI annotations (Swagger or MicroProfile OpenAPI, see RAPI-DOC-001) are present on the classpath but some controllers have no @Tag, so endpoints are not grouped in the generated documentation.
  • Recommendation: Add @Tag to controllers to group their endpoints in the OpenAPI documentation.
  • Learn more: https://springdoc.org/

RAPI-DOC-003 - Deprecated endpoints signal deprecation to HTTP clients

  • Severity: INFO
  • Detects: A handler (or its declaring class) is annotated @Deprecated but has no accompanying @Operation(deprecated = true). @Deprecated only communicates to compile-time Java consumers; HTTP clients need an OpenAPI deprecation marker and can also receive the standardized Deprecation response header defined by RFC 9745. Skipped when no OpenAPI annotations are present on the classpath (see RAPI-DOC-001).
  • Recommendation: Add @Operation(deprecated = true) alongside @Deprecated, and consider RFC 9745's Deprecation header plus RFC 8594's Sunset header for runtime notice and a concrete retirement date.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9745.html

Deliberately deferred checks

The 2026 source audit retained all 53 rule IDs and severities that were active at the time. It changed only directly observable model facts; no rule was removed or broadened from a style preference alone.

  • JAX-RS Response and RESTEasy Reactive RestResponse builder chains can set statuses and headers imperatively. The advisor does not perform interprocedural data-flow analysis just to infer those values.
  • JAX-RS sub-resource locators require a cross-class locator graph before their inherited path parameters can be analyzed reliably. The current bounded model does not attempt that graph.
  • Retained Java parameter names are not exposed by ArchUnit's imported parameter model. The advisor therefore checks explicit @PathVariable/@PathParam/@RestPath names and does not guess omitted names.
  • Reactive stream pagination needs return-media-type and runtime serialization evidence. Flux/Multi wrappers are unwrapped for payload-shape rules, but no additional pagination conclusion is inferred from a wrapper alone.
  • Generated or static OpenAPI documents are not parsed. The RAPI-DOC-* rules remain annotation-level advisories rather than claims that generated documentation is complete.
Edit this page
Last Updated: 8/22/26, 11:07 PM
Prev
Architecture
Next
Spring Advisor