@RequestMapping purpose and attributes, method-level mappings, handling different HTTP methods, @Controller vs @RestController, when to use each, how responses differ, and what @RestController means for serialization.
Published September 25, 2026
Mapping questions are about precision: which attributes exist, how a request is matched, and what gets written to the response. A short code example per answer is worth more than a definition.
@RequestMapping?Short answer: It maps web requests to controller classes and methods. It matches on the path, and optionally on the HTTP method, parameters, headers, and content types (consumes/produces). At class level it sets a shared base path. At method level it narrows the match.
@RestController
@RequestMapping("/api/customers") // base path for every method
class CustomerController {
@RequestMapping(value = "/{id}", method = RequestMethod.GET) // = @GetMapping("/{id}")
CustomerDto get(@PathVariable long id) { … }
}
Learn it in depth → REST Controllers
Short answer: Annotate each handler method with @GetMapping, @PostMapping, @PutMapping, @PatchMapping or @DeleteMapping (or @RequestMapping with method). The method's path is appended to the class-level path.
@RestController
@RequestMapping("/api/customers")
class CustomerController {
@GetMapping CustomerPage list(Pageable page) { … } // GET /api/customers
@GetMapping("/{id}") CustomerDto get(@PathVariable long id) { … } // GET /api/customers/42
@PostMapping ResponseEntity<CustomerDto> create(@Valid @RequestBody NewCustomer c) { … }
@GetMapping("/{id}/orders") List<OrderDto> orders(@PathVariable long id) { … }
}
@RequestMapping have?Short answer:
| Attribute | Purpose | Example |
|---|---|---|
value / path | URL pattern(s) | "/orders/{id}", {"/a", "/b"} |
method | HTTP method(s) | RequestMethod.POST |
params | Require (or exclude) request parameters | params = "type=premium", "!debug" |
headers | Require headers | headers = "X-API-Version=2" |
consumes | Accepted request Content-Type | MediaType.APPLICATION_JSON_VALUE |
produces | Response content type(s), matched against Accept | "application/json" |
name | A name for the mapping (used for URI building) | "getOrder" |
Key points to cover:
consumes doesn't match, the response is 415 Unsupported Media Type. If produces doesn't match the Accept header, it's 406 Not Acceptable. If the path matches but the method doesn't, it's 405 Method Not Allowed.@RequestMapping handle different HTTP methods?Short answer: Through the method attribute, or the composed shortcuts. The same path can map to different methods for different verbs. That's how REST resources are modelled.
@GetMapping("/{id}") OrderDto read(@PathVariable long id) { … }
@PutMapping("/{id}") OrderDto replace(@PathVariable long id, @RequestBody OrderDto o) { … }
@DeleteMapping("/{id}") ResponseEntity<Void> delete(@PathVariable long id) { … }
Key points to cover:
@RequestMapping without method matches every verb, including ones you never meant to support.HEAD and OPTIONS requests automatically, based on the GET mappings and the mappings available for the path.@Controller and @RestController?Short answer: @Controller is for MVC controllers, whose methods usually return a view name plus model data, rendered as HTML by a template engine. @RestController = @Controller + @ResponseBody on every method, so return values are serialised into the response body (JSON or XML).
@Controller
class PageController {
@GetMapping("/orders/{id}")
String page(@PathVariable long id, Model model) {
model.addAttribute("order", service.find(id));
return "order-details"; // → templates/order-details.html
}
}
@RestController instead of @Controller?Short answer: For APIs consumed by programs: SPAs (React, Angular), mobile apps, and other services. Use @Controller when the server renders HTML pages (Thymeleaf, JSP), or when one controller mixes pages with a few JSON endpoints (annotated with @ResponseBody).
Short answer: With @Controller, a returned String is a view name: ViewResolver → View → HTML, with the Model supplying the data. With @RestController, the returned object goes to the HttpMessageConverter chosen by content negotiation (the Accept header and produces), and is written straight to the body. There's no view resolution.
Common trap: returning a String from a @RestController method sends that literal text as the body. In a @Controller, the same return value is treated as a template name. If the template doesn't exist, you get an error or a 404.
@RestController mean for data serialisation?Short answer: Every return value is serialised by Jackson (by default), so the shape of your Java types becomes your API contract. That leads to some practical rules:
spring.jackson.serialization.write-dates-as-timestamps=false), naming, and whether null values are included.@JsonProperty, @JsonIgnore and @JsonFormat sparingly. Prefer shaping the DTO itself.record OrderDto(long id, String status, BigDecimal total, Instant createdAt) { } // explicit API shape
Q: What is content negotiation?
A: Choosing the response format based on the client's Accept header (and the handler's produces). If a client asks for XML, and an XML converter is on the classpath (jackson-dataformat-xml), the same controller can return XML.
Q: How do you read request headers or cookies?
A: Use @RequestHeader("X-Request-Id") String requestId and @CookieValue("session") String session. Both can be optional (required = false), or have a defaultValue.
Q: How are ambiguous mappings resolved? A: Spring picks the most specific match. A literal path beats a pattern, and fewer wildcards beat more. Two equally specific mappings for the same request are a startup error ("Ambiguous mapping").
Q: What's the default path matching strategy in Spring 6?
A: PathPatternParser, which is faster than the old AntPathMatcher. Trailing-slash matching is off by default, so /orders/ no longer matches /orders.