Handbooks / Spring Boot / Chapter 3
Building REST APIs
7 pages · ~4 min✓ Reviewed
Builds on Core: IoC and Dependency Injection. Next up: Spring AOP and Proxies.
REST Controller Mental Model
1. What @RestController Is
@RestController=@Controller+@ResponseBody- Every return value is written to the body by an HttpMessageConverter
- Never resolved as a view name
2. Request Flow
- DispatcherServletfront door
- HandlerMappingfinds method
- HandlerAdapterbinds arguments
- Your methodruns
- HttpMessageConverterresult to body
3. Mapping and Thin Controller
java
@RestController @RequestMapping("/api/users") class UserController { private final UserService svc; @GetMapping("/{id}") UserDto get(@PathVariable Long id) { return svc.find(id); } }
class prefix + method shortcut
- Class-level
@RequestMappingsets the prefix @GetMapping@PostMapping@PutMapping@PatchMapping@DeleteMapping=@RequestMapping(method=...)- Thin: parse and validate input, delegate to a service, return DTOs
4. Rules
Request Mapping, Path Variables and Query Params
1. Binding the URL
@GetMapping("/{id}")+@PathVariable Long idbinds the URI segment- Name must match the placeholder, else
@PathVariable("id") @RequestParam String qreads?q=...- Required by default: a missing param gives 400
- Relax with
required=falseordefaultValue="10"
2. Missing Values and Mismatches
| Missing param | Result |
|---|---|
String, required (default) | 400 |
int, required=true (default) | 400 MissingServletRequestParameterException |
Request Bodies and Bean Validation
1. Body to DTO and Failures
@RequestBody: Jackson maps the JSON body to a DTO@Valid(or@Validated) before it runs Bean Validation, before the method body
- JSON body
- Jacksonbad JSON: 400
- @Validinvalid: 400
- Method runs
| Failure | Exception | Status |
|---|---|---|
| Malformed JSON | HttpMessageNotReadableException | 400 |
@Valid body | MethodArgumentNotValidException | 400 |
ResponseEntity and Status Codes
1. What ResponseEntity Controls
ResponseEntity<T>= status + headers + body in one object- Return a plain object → status is always 200
@ResponseStatus(HttpStatus.CREATED)sets one fixed status- Status varies at runtime → use
ResponseEntity
2. Builders
| Builder | Status |
|---|---|
ResponseEntity.ok(body) | 200 |
ResponseEntity.status(HttpStatus.CREATED).body(x) | 201 |
ResponseEntity.noContent().build() | 204 |
ResponseEntity.notFound().build() |
Exception Handling and Content Negotiation
1. Global Error Handlers
- @RestControllerAdvice =
@ControllerAdvice+@ResponseBody: global handlers @ControllerAdvicealone needs@ResponseBodyon each handler@ExceptionHandler(UserNotFoundException.class)→ResponseEntity<ErrorResponse>with 404- The most specific exception type wins
java
@ExceptionHandler(MethodArgumentNotValidException.class) ResponseEntity<Map<String,String>> invalid(MethodArgumentNotValidException ex) { Map<String,String> m = new HashMap<>(); ex.getBindingResult().getFieldErrors() .forEach(f -> m.put(f.getField(), f.getDefaultMessage())); return ResponseEntity.badRequest().body(m); }
field → message map
2. Simple Mapping and Standard Body
Spring REST Cheat Sheet
1. Annotations
| Annotation | Does |
|---|---|
@RestController | controller + body |
@RequestMapping | class prefix |
@GetMapping / @PostMapping | method shortcuts |
@PathVariable | URI segment |
@RequestParam | ?q= query |
@RequestBody | JSON to DTO |
@Valid | triggers validation |
@RequestHeader | header value |
Check yourself
@GetMapping("/{id}") with get(@PathVariable Long userId). What happens on GET /5?
- No URI variable named
userId, soMissingPathVariableException - It surfaces as a 500. Fix:
@PathVariable("id").
create(@RequestBody CreateUser req) where name is @NotBlank. POST {"name":""} gives?
- Constraints are ignored without
@Valid, so the method runs normally - With
@Valid:MethodArgumentNotValidException, a 400
A @Controller method returns the String "ok". Why isn't the body ok?
- Without
@ResponseBody, "ok" is a view name @RestControllerwrites it via an HttpMessageConverter