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
    • Spring Security Advisor
    • Memory advisor
    • Pentesting
    • GraalVM readiness
    • CRaC readiness
    • Quarkus Application Advisor
    • Quarkus Security Advisor

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 so the same 51 rules run identically on Spring Boot and Quarkus. 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 (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-only types with no JAX-RS equivalent (RFC 9457 ProblemDetail); on Quarkus those report SKIPPED with an explanatory reason instead of misfiring — see 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 and degrades to a SKIPPED/ERROR outcome so one unresolvable class never aborts the scan, 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 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 51 rules across 8 categories (8 HIGH, 10 MEDIUM, 18 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, honored by Quarkus's quarkus-smallrye-openapi) on either framework; otherwise they are reported as SKIPPED.


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-style handler mapped to GET can be triggered by crawlers, prefetching, or caching.
  • 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 servlet response 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 new resource.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html#section-15.3.2

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. The Idempotency-Key HTTP header is still an IETF HTTPAPI working-group Internet-Draft, but it formalises a pattern already used by Stripe, PayPal, and Azure.
  • 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 handler that accepts a Pageable but returns a raw List/array discards the paging metadata (total elements, total pages) that Page or Slice would carry.
  • Recommendation: Return Page or Slice when the handler accepts a Pageable, so paging metadata reaches the client.
  • 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.
  • 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 ProblemDetail produce non-standard error payloads. Not applicable on JAX-RS/Quarkus: Spring's ProblemDetail type has no equivalent there, so the rule reports SKIPPED.
  • 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 present (so every error - validation, not-found, server fault - collapses to one status), or it maps to a fixed non-5xx status. A broad catch-all mapped to a 5xx status (RFC 9110 §15.6.1) that coexists with specific handlers for other exception types is a deliberate, correct 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 ProblemDetail (RFC 9457) for error responses but some application exception classes are annotated with @ResponseStatus instead, mixing error-handling approaches. Not applicable on JAX-RS/Quarkus: Spring's ProblemDetail type has no equivalent there, so the rule reports SKIPPED.
  • 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.
  • Learn more: https://www.rfc-editor.org/rfc/rfc9110.html#section-10.2.3

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 calling the endpoint directly need an out-of-band signal (RFC 8594 Sunset header, or the still-draft Deprecation header). Skipped when no OpenAPI annotations are present on the classpath (see RAPI-DOC-001).
  • Recommendation: Add @Operation(deprecated = true) alongside @Deprecated so the generated OpenAPI document signals deprecation to HTTP clients, and consider RFC 8594's Sunset header for a concrete retirement date.
  • Learn more: https://www.rfc-editor.org/rfc/rfc8594.html
Edit this page
Last Updated: 7/5/26, 12:38 PM
Prev
Architecture
Next
Spring Advisor