All Spring templates

Spring MVC Rules

Controllers, ResponseEntity, validation, exception handling, and content negotiation.

DevZone Tools1,520 copiesUpdated Mar 4, 2026SpringJava
# CLAUDE.md — Spring MVC

## Controllers

- `@RestController` for JSON APIs. `@Controller` only when you actually return view names.
- Constructor-inject services. Final fields, no `@Autowired`.
- Map URLs at the class level: `@RequestMapping("/users")`. Methods use the verb-specific shortcuts (`@GetMapping`, `@PostMapping`).
- One controller per resource. Cross-cutting endpoints (`/health`, `/metrics`) live in their own controller.

## Request handling

- `@RequestBody` for JSON bodies. Add `@Valid` to trigger Bean Validation.
- `@PathVariable` and `@RequestParam` are explicit — name them with the parameter name when it differs:
  ```java
  @GetMapping("/{id}") UserDto get(@PathVariable("id") String userId)
  ```
- Use `Optional<T>` for `@RequestParam(required = false)` instead of nullable defaults.

## Response shaping

- Return `ResponseEntity<T>` when you need to set status, headers, or vary by branch:
  ```java
  return ResponseEntity.created(uri).body(dto);
  ```
- Return the DTO directly when status is always 200. Spring fills in the rest.
- Set `produces = MediaType.APPLICATION_JSON_VALUE` when you mean it. Avoid `MediaType.ALL_VALUE` on data endpoints.

## Validation

- Bean Validation on the request DTO with `jakarta.validation.constraints.*`:
  ```java
  record CreateUser(@NotBlank @Email String email, @NotBlank @Size(min = 8) String password) {}
  ```
- `@Valid` on the controller parameter. Spring throws `MethodArgumentNotValidException` on failure.
- For groups (`Default`, `OnCreate`, `OnUpdate`), use `@Validated(OnCreate.class)`.

## Exception handling

- `@RestControllerAdvice` class with `@ExceptionHandler` methods. One per error type.
- Map domain exceptions to status codes here, never in controllers:
  ```java
  @ExceptionHandler(NotFoundException.class)
  ResponseEntity<ProblemDetail> handle(NotFoundException e) {
      return ResponseEntity.status(404).body(ProblemDetail.forStatusAndDetail(404, e.getMessage()));
  }
  ```
- Use `ProblemDetail` (RFC 7807) for error responses. It's the modern Spring default.

## Content negotiation

- Default to JSON. Add XML/Other media types only when a client actually needs them.
- Don't use `produces` to "version" APIs (`application/vnd.app.v1+json`). Use URL versioning for clarity.

## Filters & interceptors

- Servlet filters for cross-cutting on the request boundary (request id, logging).
- `HandlerInterceptor` for Spring-aware concerns (auth, MDC).
- Don't put business logic in filters. They're for cross-cutting only.

## Don't

- Don't return `null` from a controller. Use `ResponseEntity.notFound().build()` or throw.
- Don't catch `Exception` in a controller method to translate it. Use `@ExceptionHandler` so the same logic applies app-wide.
- Don't expose entity classes from controllers. Map to DTOs.
- Don't mix `@RequestParam` and `@PathVariable` carelessly — they bind from different sources and the bug is silent.

Other Spring templates