REST controllers and request mapping

Spring Boot · Essentials · 14 min

REST APIs are the most common thing built with Spring Boot.

A class annotated @RestController handles HTTP requests; each method's return value is serialised to the response body, as JSON by default via Jackson. @RequestMapping("/api/books") on the class sets a common path prefix.

Methods are mapped with @GetMapping, @PostMapping, @PutMapping, @PatchMapping and @DeleteMapping. Parameters bind parts of the request:

  • @PathVariable Long id takes {id} from the path /api/books/{id} and converts it to the declared type; a failed conversion gives a 400.
  • @RequestParam(defaultValue = "0") int page reads ?page=2. Parameters are required unless they have a default or required = false (or are Optional).
  • @RequestHeader reads headers.

Return a domain object, a List, or a Java record: Jackson turns record components into JSON fields. To control status and headers, return ResponseEntity<T>: ResponseEntity.ok(book) or ResponseEntity.notFound().build().

Controllers should be thin: translate HTTP to method calls on a @Service and back. Business logic in controllers is hard to test and reuse.

Spring MVC runs each request on a thread from Tomcat's pool (or a virtual thread if enabled), so controllers must be stateless, like all singleton beans.

Example

record Book(long id, String title, String author) {}

@RestController
@RequestMapping("/api/books")
class BookController {
    private final Map<Long, Book> books = Map.of(
        1L, new Book(1, "Dune", "Herbert"),
        2L, new Book(2, "Emma", "Austen"));

    @GetMapping
    List<Book> list(@RequestParam(defaultValue = "") String author) {
        return books.values().stream()
            .filter(b -> author.isEmpty() || b.author().equalsIgnoreCase(author))
            .toList();
    }

    @GetMapping("/{id}")
    ResponseEntity<Book> get(@PathVariable long id) {
        Book b = books.get(id);
        return b == null ? ResponseEntity.notFound().build() : ResponseEntity.ok(b);
    }
}
// GET /api/books?author=austen -> [{"id":2,"title":"Emma","author":"Austen"}]
// GET /api/books/7 -> 404    GET /api/books/abc -> 400

Remember

  • @RestController methods return objects serialised to JSON
  • @PathVariable binds path segments, @RequestParam query parameters
  • Return ResponseEntity to control status codes and headers
  • Keep controllers thin; delegate to services

Watch out for

  • Returning null for a missing resource, which gives 200 with an empty body instead of 404.

Check yourself

  1. A method has @RequestParam int page with no default and the request has no page parameter. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.spring.io/spring-boot/reference/web/servlet.html