PrepZone Logo
PrepZone

Mapping HTTP to Java Methods

@RestController, @GetMapping, @PathVariable, @RequestBody and the rest of the mapping toolkit.

Why this matters

  • Mapping annotations are the daily vocabulary of BookStore API development — knowing the full toolkit avoids workarounds and bugs.
  • Incorrect binding (missing @RequestBody, wrong @PathVariable name) causes silent 400 errors or null parameters.
  • HTTP semantics (status codes, content types) belong in the controller layer, not services.
HTTP request
DispatcherServlet
Controller
Service
JSON response
From HTTP arrival to JSON response — know where validation, security and exception handling sit.

Core mapping annotations

Java
@RestController
@RequestMapping("/api/books")
public class BookController {
    private final BookService bookService;

    public BookController(BookService bookService) {
        this.bookService = bookService;
    }

    @GetMapping
    public List<BookResponse> list(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        return bookService.findAll(page, size);
    }

    @GetMapping("/{id}")
    public BookResponse get(@PathVariable Long id) {
        return BookResponse.from(bookService.findById(id));
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public BookResponse create(@RequestBody CreateBookRequest request) {
        return BookResponse.from(bookService.create(request));
    }

    @PutMapping("/{id}")
    public BookResponse update(@PathVariable Long id,
                               @RequestBody UpdateBookRequest request) {
        return BookResponse.from(bookService.update(id, request));
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) {
        bookService.delete(id);
    }
}

Binding annotations

  • @PathVariable — URI segment: /api/books/{id} → Long id.
  • @RequestParam — Query string: ?genre=fiction → String genre.
  • @RequestBody — Deserialise JSON body to a Java object.
  • @RequestHeader — Read an HTTP header value.
  • @ResponseStatus — Set HTTP status without returning ResponseEntity.

ResponseEntity for full control

When you need custom headers or conditional status codes:

Java
@GetMapping("/{id}")
public ResponseEntity<BookResponse> get(@PathVariable Long id) {
    return bookService.findById(id)
        .map(book -> ResponseEntity.ok(BookResponse.from(book)))
        .orElse(ResponseEntity.notFound().build());
}

ResponseEntity wraps body, status, and headers in one return type.

Content negotiation

Java
@GetMapping(value = "/{id}/export", produces = MediaType.APPLICATION_XML_VALUE)
public BookXml export(@PathVariable Long id) {
    return bookService.toXml(id);
}

produces and consumes filter by Content-Type and Accept headers.

Path variable patterns

Java
@GetMapping("/genre/{genre}/books")
public List<BookResponse> byGenre(@PathVariable String genre) { ... }

@GetMapping("/search/{query:.+}")
public List<BookResponse> search(@PathVariable String query) { ... }

The {query:.+} regex allows dots in the path segment — useful for ISBN lookups like /search/978-0-13-235088-4.

Request body with validation

Java
@PostMapping
public BookResponse create(@RequestBody @Valid CreateBookRequest request) {
    return BookResponse.from(bookService.create(request));
}

@Valid triggers Bean Validation on the request DTO. Invalid input returns 400 before the service method runs.

HTTP method semantics

MethodIdempotentSafeBookStore use
GETYesYesList, retrieve
POSTNoNoCreate
PUTYesNoFull update
PATCHNoNoPartial update
DELETEYesNoRemove
  • GET

    IdempotentYes
    SafeYes
    BookStore useList, retrieve
  • POST

    IdempotentNo
    SafeNo
    BookStore useCreate
  • PUT

    IdempotentYes
    SafeNo
    BookStore useFull update
  • PATCH

    IdempotentNo
    SafeNo
    BookStore usePartial update
  • DELETE

    IdempotentYes
    SafeNo
    BookStore useRemove

Use the correct verb — GET for reads, POST for creates, PUT for replacements, DELETE for removal.

Class-level vs method-level mapping

Java
@RestController
@RequestMapping("/api/v1/books")  // applies to all methods
public class BookController {
    @GetMapping("/{id}")  // resolves to /api/v1/books/{id}
    public BookResponse get(@PathVariable Long id) { ... }
}

Version in the class-level path (/api/v1/) enables future /api/v2/ without breaking existing clients.

CORS preflight

Browsers send OPTIONS before cross-origin requests. Spring MVC handles this when CORS is configured — covered in the security module.

Quick recall

Everything you need if you only revisit this box.

  • @RestController = @Controller + @ResponseBody for JSON APIs.
  • @PathVariable binds URI segments; @RequestParam binds query parameters.
  • @RequestBody deserialises JSON; always pair with @Valid on input DTOs.
  • ResponseEntity provides full control over status, headers, and body.
  • Use correct HTTP verbs: GET reads, POST creates, PUT replaces, DELETE removes.
  • Class-level @RequestMapping sets the base path for all handler methods.

Test yourself

Answer these before moving on — recall is what makes it stick.