Week 4 Day 2 — Request Mapping Deep Dive
Goal
Today I want to understand request mapping deeply.
Main questions:
- What is
@RequestMapping? - What are
@GetMapping,@PostMapping,@PutMapping,@PatchMapping, and@DeleteMapping? - How do class-level and method-level mappings combine?
- What is
@PathVariable? - What is
@RequestParam? - What is
@RequestHeader? - What are
paramsandheadersmapping conditions? - What are
consumesandproduces? - What is content negotiation?
- What are common exam traps?
1. Quick Review from Week 4 Day 1
In Day 1, I learned:
- Spring MVC maps HTTP requests to Java controller methods.
DispatcherServletis the front controller.HandlerMappingfinds the correct handler.HandlerAdapterinvokes the handler.@RestControllermeans@Controllerplus@ResponseBody.@RequestBodyreads the HTTP body.@PathVariablereads path values.@RequestParamreads query parameters.HttpMessageConverterconverts HTTP bodies to Java objects and Java objects to HTTP bodies.
Memory sentence:
Request -> DispatcherServlet -> HandlerMapping -> Controller -> Response
Today I go deeper into how Spring chooses the correct controller method.
2. What Is @RequestMapping?
@RequestMapping maps web requests to controller classes or methods.
Simple definition:
@RequestMappingtells Spring MVC which HTTP request should be handled by which controller method.
Example:
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@RequestMapping("/hello")
public String hello() {
return "Hello";
}
}
This handles:
GET /api/tasks/hello
POST /api/tasks/hello
PUT /api/tasks/hello
Why?
Because no HTTP method was specified.
Important:
If no HTTP method is specified,
@RequestMappingcan match multiple HTTP methods.
3. Better: Specify the HTTP Method
Instead of this:
@RequestMapping("/api/tasks")
public List<TaskDto> list() {
return taskService.findAll();
}
Better:
@GetMapping("/api/tasks")
public List<TaskDto> list() {
return taskService.findAll();
}
Why?
Because the method clearly handles:
GET /api/tasks
not every possible HTTP method.
Memory sentence:
Prefer specific mapping annotations like
@GetMappingand@PostMappingin REST controllers.
4. @RequestMapping Can Match Many Conditions
A request mapping can match by:
path
HTTP method
query parameters
headers
consumes content type
produces content type
Example:
@RequestMapping(
path = "/api/tasks",
method = RequestMethod.POST,
consumes = "application/json",
produces = "application/json"
)
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
This means:
Path must be /api/tasks.
HTTP method must be POST.
Request body must be JSON.
Response will be JSON.
5. Shortcut Mapping Annotations
Spring provides shortcut annotations:
@GetMapping
@PostMapping
@PutMapping
@PatchMapping
@DeleteMapping
They are shortcuts for @RequestMapping(method = ...).
Example:
@GetMapping("/api/tasks")
is a shortcut for:
@RequestMapping(
path = "/api/tasks",
method = RequestMethod.GET
)
Memory sentence:
Shortcut mappings are clearer for REST APIs.
6. HTTP Method Meaning in REST
| HTTP Method | Common REST Meaning |
|---|---|
GET | read data |
POST | create a new resource or execute command |
PUT | replace a resource |
PATCH | partially update a resource |
DELETE | delete a resource |
Example:
GET /api/tasks -> list tasks
GET /api/tasks/123 -> get one task
POST /api/tasks -> create task
PUT /api/tasks/123 -> replace task
PATCH /api/tasks/123 -> update part of task
DELETE /api/tasks/123 -> delete task
7. Full REST Mapping Example
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@GetMapping
public List<TaskDto> list() {
return taskService.findAll();
}
@GetMapping("/{id}")
public TaskDto get(@PathVariable Long id) {
return taskService.findById(id);
}
@PostMapping
public ResponseEntity<TaskDto> create(@RequestBody CreateTaskRequest request) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping("/{id}")
public TaskDto replace(
@PathVariable Long id,
@RequestBody ReplaceTaskRequest request
) {
return taskService.replace(id, request);
}
@PatchMapping("/{id}")
public TaskDto update(
@PathVariable Long id,
@RequestBody UpdateTaskRequest request
) {
return taskService.update(id, request);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
taskService.delete(id);
return ResponseEntity.noContent().build();
}
}
Mappings:
GET /api/tasks -> list()
GET /api/tasks/123 -> get(123)
POST /api/tasks -> create(request)
PUT /api/tasks/123 -> replace(123, request)
PATCH /api/tasks/123 -> update(123, request)
DELETE /api/tasks/123 -> delete(123)
8. Class-Level and Method-Level Mapping
Class-level mapping:
@RequestMapping("/api/tasks")
Method-level mapping:
@GetMapping("/{id}")
Combined:
GET /api/tasks/{id}
Example:
@RestController
@RequestMapping("/api/clients")
public class ClientController {
@GetMapping("/{id}")
public ClientDto getClient(@PathVariable Long id) {
return clientService.findById(id);
}
}
This handles:
GET /api/clients/10
Memory sentence:
Class-level path plus method-level path creates the final endpoint path.
9. Empty Method Mapping
Example:
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@GetMapping
public List<TaskDto> list() {
return taskService.findAll();
}
}
@GetMapping has no path.
It uses the class-level path:
GET /api/tasks
This is common and clean.
10. Avoid Repeating Full Paths
Less clean:
@RestController
public class TaskController {
@GetMapping("/api/tasks")
public List<TaskDto> list() {
return taskService.findAll();
}
@GetMapping("/api/tasks/{id}")
public TaskDto get(@PathVariable Long id) {
return taskService.findById(id);
}
}
Cleaner:
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@GetMapping
public List<TaskDto> list() {
return taskService.findAll();
}
@GetMapping("/{id}")
public TaskDto get(@PathVariable Long id) {
return taskService.findById(id);
}
}
Memory sentence:
Put the common base path on the controller class.
11. @PathVariable
@PathVariable reads a value from the URL path.
Request:
GET /api/tasks/123
Controller:
@GetMapping("/api/tasks/{id}")
public TaskDto getTask(@PathVariable Long id) {
return taskService.findById(id);
}
Spring binds:
123 -> id
12. Multiple Path Variables
Request:
GET /api/clients/10/tasks/99
Controller:
@GetMapping("/api/clients/{clientId}/tasks/{taskId}")
public TaskDto getClientTask(
@PathVariable Long clientId,
@PathVariable Long taskId
) {
return taskService.findClientTask(clientId, taskId);
}
Spring binds:
clientId = 10
taskId = 99
13. Path Variable Name Differs from Parameter Name
Path:
@GetMapping("/api/tasks/{taskId}")
Parameter:
public TaskDto getTask(@PathVariable("taskId") Long id) {
return taskService.findById(id);
}
Spring binds:
URL variable taskId -> Java parameter id
Use this when Java parameter name differs from path variable name.
14. Type Conversion for Path Variables
Path variables are text in the URL.
Example:
GET /api/tasks/123
Spring receives:
"123"
Controller expects:
@PathVariable Long id
Spring converts:
"123" -> Long 123
If conversion fails:
GET /api/tasks/abc
Spring cannot convert:
"abc" -> Long
Usually response:
400 Bad Request
Memory sentence:
Path variables are strings first, then Spring converts them to the target type.
15. @RequestParam
@RequestParam reads query parameters.
Request:
GET /api/tasks?page=0&size=20
Controller:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam int page,
@RequestParam int size
) {
return taskService.findPage(page, size);
}
Spring binds:
page=0 -> page
size=20 -> size
16. Required Request Parameters
By default, @RequestParam is required.
Example:
@GetMapping("/api/tasks")
public List<TaskDto> list(@RequestParam String status) {
return taskService.findByStatus(status);
}
Request:
GET /api/tasks
No status parameter.
Usually response:
400 Bad Request
Because required query parameter is missing.
17. Optional Request Parameters
Option 1: required = false
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam(required = false) String status
) {
return taskService.findByStatus(status);
}
If missing:
status = null
Option 2: Optional
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam Optional<String> status
) {
return taskService.findByStatus(status);
}
Option 3: default value
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam(defaultValue = "OPEN") String status
) {
return taskService.findByStatus(status);
}
If missing:
status = OPEN
Memory sentence:
@RequestParamis required by default unless made optional or given a default value.
18. Request Parameter Name Differs from Java Parameter
Request:
GET /api/tasks?task_status=OPEN
Controller:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam("task_status") String status
) {
return taskService.findByStatus(status);
}
Spring binds:
task_status -> status
19. RequestParam Type Conversion
Request:
GET /api/tasks?page=0&size=20&archived=false
Controller:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam int page,
@RequestParam int size,
@RequestParam boolean archived
) {
return taskService.find(page, size, archived);
}
Spring converts:
"0" -> int 0
"20" -> int 20
"false" -> boolean false
If conversion fails:
GET /api/tasks?page=abc
Usually response:
400 Bad Request
20. Multiple Values for Same Query Parameter
Request:
GET /api/tasks?status=OPEN&status=DONE
Controller:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam List<String> status
) {
return taskService.findByStatuses(status);
}
Spring binds:
status = ["OPEN", "DONE"]
This is useful for filters.
21. @RequestHeader
@RequestHeader reads HTTP headers.
Request:
GET /api/tasks
X-Tenant-Id: tenant-123
Controller:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestHeader("X-Tenant-Id") String tenantId
) {
return taskService.findForTenant(tenantId);
}
Spring binds:
X-Tenant-Id -> tenantId
22. Optional Request Header
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestHeader(value = "X-Request-Id", required = false) String requestId
) {
return taskService.findAll(requestId);
}
If header is missing:
requestId = null
With default value:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestHeader(value = "X-Client-Version", defaultValue = "unknown") String clientVersion
) {
return taskService.findAll(clientVersion);
}
23. @RequestBody
@RequestBody reads the HTTP request body.
Request:
POST /api/tasks
Content-Type: application/json
{
"title": "Learn Spring MVC"
}
Controller:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Spring uses an HttpMessageConverter to convert JSON into:
CreateTaskRequest
DTO:
public record CreateTaskRequest(String title) {
}
Memory sentence:
@RequestBodymeans body to Java object.
24. @RequestBody Is Usually for POST, PUT, PATCH
Common use:
POST -> create body
PUT -> replace body
PATCH -> partial update body
Less common:
GET with body
In REST APIs, avoid GET request bodies.
Use query parameters for filtering and pagination.
Example:
GET /api/tasks?status=OPEN&page=0&size=20
not:
GET /api/tasks
{
"status": "OPEN"
}
Memory sentence:
Use query parameters for GET filters. Use request body for create/update commands.
25. One @RequestBody Per Method
Usually one controller method has one request body.
Bad:
@PostMapping("/api/tasks")
public TaskDto create(
@RequestBody CreateTaskRequest request,
@RequestBody Metadata metadata
) {
return taskService.create(request, metadata);
}
Why bad?
The HTTP request has one body.
Spring cannot normally read two separate request bodies into two objects.
Better:
public record CreateTaskCommand(
String title,
Metadata metadata
) {
}
Controller:
@PostMapping("/api/tasks")
public TaskDto create(@RequestBody CreateTaskCommand command) {
return taskService.create(command);
}
Memory sentence:
One HTTP request has one body, so use one
@RequestBodyobject.
26. Mapping with Query Parameter Conditions: params
params in mapping means:
This method should match only if specific query parameters are present or have certain values.
Example:
@GetMapping(value = "/api/tasks", params = "status")
public List<TaskDto> listByStatus(@RequestParam String status) {
return taskService.findByStatus(status);
}
This matches:
GET /api/tasks?status=OPEN
But not:
GET /api/tasks
27. Mapping by Parameter Value
@GetMapping(value = "/api/tasks", params = "status=OPEN")
public List<TaskDto> openTasks() {
return taskService.findOpenTasks();
}
This matches:
GET /api/tasks?status=OPEN
But not:
GET /api/tasks?status=DONE
28. Mapping by Missing Parameter
@GetMapping(value = "/api/tasks", params = "!status")
public List<TaskDto> listAll() {
return taskService.findAll();
}
This matches:
GET /api/tasks
But not:
GET /api/tasks?status=OPEN
Memory sentence:
paramsis a mapping condition.@RequestParamreads the value.
29. params vs @RequestParam
This is an important exam trap.
params
Used in mapping selection.
@GetMapping(value = "/api/tasks", params = "status")
Meaning:
This method matches only if the request has status parameter.
@RequestParam
Used to bind the value to a method parameter.
public List<TaskDto> list(@RequestParam String status)
Meaning:
Read status value into Java parameter.
Memory sentence:
paramschooses the method.@RequestParamreads the value.
30. Mapping with Header Conditions: headers
headers in mapping means:
This method should match only if specific headers are present or have certain values.
Example:
@GetMapping(value = "/api/tasks", headers = "X-API-Version=1")
public List<TaskDto> listV1() {
return taskService.findAllV1();
}
This matches:
GET /api/tasks
X-API-Version: 1
But not:
GET /api/tasks
X-API-Version: 2
31. Header Present Condition
@GetMapping(value = "/api/tasks", headers = "X-Tenant-Id")
public List<TaskDto> listForTenant(
@RequestHeader("X-Tenant-Id") String tenantId
) {
return taskService.findForTenant(tenantId);
}
This method matches only if the request has:
X-Tenant-Id
header.
Memory sentence:
headerschooses the method.@RequestHeaderreads the header value.
32. headers vs @RequestHeader
headers
Mapping condition:
@GetMapping(value = "/api/tasks", headers = "X-API-Version=2")
@RequestHeader
Value binding:
public List<TaskDto> list(@RequestHeader("X-API-Version") String version)
Memory sentence:
headerscontrols matching.@RequestHeaderreads data.
33. consumes
consumes means:
This method accepts requests with a specific
Content-Type.
Example:
@PostMapping(
value = "/api/tasks",
consumes = "application/json"
)
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
This matches:
POST /api/tasks
Content-Type: application/json
But not:
POST /api/tasks
Content-Type: text/plain
Memory sentence:
consumeschecks the request body content type.
34. Content-Type Header
consumes uses the request Content-Type.
Example:
Content-Type: application/json
means:
The request body is JSON.
If controller says:
consumes = "application/json"
then the request must send compatible JSON content type.
35. Wrong Content-Type
Controller:
@PostMapping(
value = "/api/tasks",
consumes = "application/json"
)
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Request:
POST /api/tasks
Content-Type: text/plain
hello
Usually response:
415 Unsupported Media Type
Because the server does not support that request body media type for this endpoint.
36. produces
produces means:
This method can produce a specific response media type.
Example:
@GetMapping(
value = "/api/tasks",
produces = "application/json"
)
public List<TaskDto> list() {
return taskService.findAll();
}
This means the method produces JSON.
Client request:
GET /api/tasks
Accept: application/json
Spring can return JSON.
Memory sentence:
producesdescribes the response content type.
37. Accept Header
The client can send:
Accept: application/json
Meaning:
I want JSON response.
The server method can declare:
produces = "application/json"
Meaning:
I can produce JSON.
If client asks for something the server cannot produce, response may be:
406 Not Acceptable
38. consumes vs produces
| Mapping Attribute | Looks at | Meaning |
|---|---|---|
consumes | request Content-Type | what the endpoint accepts |
produces | request Accept header / response type | what the endpoint returns |
Memory sentence:
consumes = request body format.
produces = response body format.
39. JSON Controller Example with Consumes and Produces
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> create(
@RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
This endpoint:
accepts JSON request body
returns JSON response body
40. MediaType Constants
Instead of hardcoding:
"application/json"
Use:
MediaType.APPLICATION_JSON_VALUE
Example:
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
This avoids typos.
41. Content Negotiation
Content negotiation means:
Spring chooses the response format based on what the client asks for and what the server can produce.
Client:
Accept: application/json
Controller:
@GetMapping(value = "/api/tasks", produces = "application/json")
Spring returns JSON.
If multiple converters and formats exist, Spring chooses the best match.
For normal REST APIs:
JSON is the most common response format.
42. Multiple Methods Same Path Different Produces
Example:
@GetMapping(value = "/api/report", produces = "application/json")
public ReportDto reportJson() {
return reportService.getReport();
}
@GetMapping(value = "/api/report", produces = "text/csv")
public String reportCsv() {
return reportService.getReportCsv();
}
Request:
GET /api/report
Accept: application/json
calls:
reportJson()
Request:
GET /api/report
Accept: text/csv
calls:
reportCsv()
43. Ambiguous Mapping
Bad:
@GetMapping("/api/tasks")
public List<TaskDto> list1() {
return taskService.findAll();
}
@GetMapping("/api/tasks")
public List<TaskDto> list2() {
return taskService.findRecent();
}
Both match:
GET /api/tasks
Spring cannot decide.
Usually startup fails with an ambiguous mapping error.
Fix by making mappings different:
@GetMapping("/api/tasks")
public List<TaskDto> list() {
return taskService.findAll();
}
@GetMapping("/api/tasks/recent")
public List<TaskDto> recent() {
return taskService.findRecent();
}
or add conditions carefully.
Memory sentence:
Two identical mappings cause ambiguity.
44. Path Design: Resource-Oriented URLs
Good REST paths:
/api/tasks
/api/tasks/{id}
/api/clients/{clientId}/tasks
/api/clients/{clientId}/tasks/{taskId}
Less good:
/api/getTask
/api/createTask
/api/deleteTask
Why?
Because HTTP method already describes the action.
Good:
GET /api/tasks/1
POST /api/tasks
DELETE /api/tasks/1
Memory sentence:
Use nouns in URLs and HTTP methods for actions.
45. Query Parameters for Filtering, Sorting, Pagination
Good:
GET /api/tasks?status=OPEN&page=0&size=20&sort=createdAt,desc
Controller:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(defaultValue = "createdAt,desc") String sort
) {
return taskService.find(status, page, size, sort);
}
Use query parameters for:
filtering
sorting
pagination
search
optional options
46. Path Variables for Resource Identity
Use path variables for resource identity.
Good:
GET /api/tasks/123
because 123 identifies the task.
Good:
GET /api/clients/10/tasks/99
because 10 identifies the client and 99 identifies the task.
Memory sentence:
Path variables identify resources. Query params filter resources.
47. PathVariable vs RequestParam
| Situation | Use |
|---|---|
| Identify one resource | @PathVariable |
| Filter list | @RequestParam |
| Pagination | @RequestParam |
| Sorting | @RequestParam |
| Search keyword | @RequestParam |
| Nested resource ID | @PathVariable |
Examples:
GET /api/tasks/123
Use:
@PathVariable Long id
GET /api/tasks?status=OPEN
Use:
@RequestParam String status
48. Headers for Metadata
Use headers for request metadata.
Examples:
Authorization
X-Request-Id
X-Correlation-Id
X-Tenant-Id
Accept-Language
Content-Type
Accept
Example:
@GetMapping("/api/tasks")
public List<TaskDto> list(
@RequestHeader("X-Correlation-Id") String correlationId
) {
return taskService.findAll(correlationId);
}
Do not use headers for normal resource identity when path variables are clearer.
49. Common HTTP Headers
| Header | Meaning |
|---|---|
Content-Type | format of request body |
Accept | response format client wants |
Authorization | authentication credentials |
Accept-Language | preferred language |
X-Request-Id | request tracing |
X-Correlation-Id | distributed tracing |
X-Tenant-Id | tenant context in multi-tenant apps |
50. Real Example for Klarsync-Like App
@RestController
@RequestMapping("/api/clients/{clientId}/tasks")
public class ClientTaskController {
@GetMapping
public List<TaskDto> listTasks(
@PathVariable Long clientId,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestHeader(value = "X-Request-Id", required = false) String requestId
) {
return taskService.findClientTasks(clientId, status, page, size, requestId);
}
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> createTask(
@PathVariable Long clientId,
@RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.createClientTask(clientId, request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
Request:
GET /api/clients/10/tasks?status=OPEN&page=0&size=20
X-Request-Id: req-123
Bindings:
clientId = 10
status = OPEN
page = 0
size = 20
requestId = req-123
51. Common Response Codes Related to Mapping
| Situation | Typical Status |
|---|---|
| No matching path | 404 Not Found |
| Path exists but HTTP method wrong | 405 Method Not Allowed |
| Request body content type unsupported | 415 Unsupported Media Type |
| Client asks for unsupported response type | 406 Not Acceptable |
| Missing required query parameter | 400 Bad Request |
| Path variable conversion fails | 400 Bad Request |
| Invalid JSON body | 400 Bad Request |
52. Common Exam Traps
Trap 1
@RequestMapping without method can match many HTTP methods.
Prefer @GetMapping, @PostMapping, etc.
Trap 2
Class-level and method-level paths combine.
@RequestMapping("/api/tasks")
@GetMapping("/{id}")
means:
GET /api/tasks/{id}
Trap 3
@PathVariable reads from the URL path.
@RequestParam reads from the query string.
@RequestHeader reads from headers.
@RequestBody reads from the body.
Trap 4
params is a mapping condition.
@RequestParam is a value binding annotation.
Trap 5
headers is a mapping condition.
@RequestHeader is a value binding annotation.
Trap 6
consumes checks request Content-Type.
produces checks what response type the method can produce.
Trap 7
Wrong Content-Type can cause 415 Unsupported Media Type.
Unsupported Accept can cause 406 Not Acceptable.
Trap 8
Two identical mappings can cause ambiguous mapping errors.
Trap 9
@RequestParam is required by default.
Trap 10
Use query parameters for filtering and pagination.
Use path variables for resource identity.
53. Real Exam Question: @RequestMapping
Question:
What does @RequestMapping do?
Answer:
@RequestMapping maps HTTP requests to controller classes or methods. It can match based on path, HTTP method, request parameters, headers, consumed media type, and produced media type.
54. Real Exam Question: Shortcut Annotations
Question:
What are @GetMapping and @PostMapping?
Answer:
They are shortcut composed annotations for @RequestMapping with a specific HTTP method. @GetMapping maps GET requests, and @PostMapping maps POST requests.
55. Real Exam Question: Class-Level Mapping
Question:
What is the final path?
@RestController
@RequestMapping("/api/tasks")
public class TaskController {
@GetMapping("/{id}")
public TaskDto get(@PathVariable Long id) {
return taskService.findById(id);
}
}
Answer:
GET /api/tasks/{id}
56. Real Exam Question: @PathVariable
Question:
What does @PathVariable do?
Answer:
@PathVariable binds a value from the URL path to a controller method parameter.
57. Real Exam Question: @RequestParam
Question:
What does @RequestParam do?
Answer:
@RequestParam binds a query parameter from the request URL to a controller method parameter.
58. Real Exam Question: Required Query Param
Question:
What happens if a required @RequestParam is missing?
Answer:
Spring usually returns 400 Bad Request.
59. Real Exam Question: @RequestHeader
Question:
What does @RequestHeader do?
Answer:
@RequestHeader binds an HTTP request header value to a controller method parameter.
60. Real Exam Question: params
Question:
What does this mean?
@GetMapping(value = "/api/tasks", params = "status")
Answer:
This method matches only requests to /api/tasks that include a status query parameter.
61. Real Exam Question: headers
Question:
What does this mean?
@GetMapping(value = "/api/tasks", headers = "X-API-Version=2")
Answer:
This method matches only requests with header X-API-Version equal to 2.
62. Real Exam Question: consumes
Question:
What does consumes = "application/json" mean?
Answer:
It means the endpoint accepts requests whose body has Content-Type: application/json.
63. Real Exam Question: produces
Question:
What does produces = "application/json" mean?
Answer:
It means the endpoint can produce a JSON response.
64. Real Exam Question: consumes vs produces
Question:
What is the difference between consumes and produces?
Answer:
consumes describes what request body media type the endpoint accepts, based on Content-Type. produces describes what response media type the endpoint can return, based on content negotiation and the Accept header.
65. Real Exam Question: Ambiguous Mapping
Question:
What happens if two controller methods have the exact same mapping?
Answer:
Spring usually fails to start with an ambiguous mapping error because it cannot decide which method should handle the request.
66. Interview Answer
Question:
Explain request mapping in Spring MVC.
Good answer:
Request mapping is how Spring MVC maps HTTP requests to controller methods. A mapping can match by path, HTTP method, query parameters, headers, request content type through consumes, and response content type through produces. In REST controllers, I usually use shortcut annotations like @GetMapping, @PostMapping, @PutMapping, @PatchMapping, and @DeleteMapping.
67. Interview Answer
Question:
What is the difference between
@PathVariable,@RequestParam,@RequestHeader, and@RequestBody?
Good answer:
@PathVariable reads values from the URL path, such as /tasks/{id}. @RequestParam reads query parameters, such as /tasks?page=0. @RequestHeader reads HTTP headers, such as X-Request-Id. @RequestBody reads the HTTP request body and converts it into a Java object, usually from JSON.
68. Interview Answer
Question:
What is the difference between
paramsand@RequestParam?
Good answer:
params is a mapping condition. It controls whether a controller method matches a request. For example, params = "status" means the method only matches if the request has a status query parameter. @RequestParam is used after the method is selected to bind the query parameter value to a Java method parameter.
69. Interview Answer
Question:
What is the difference between
consumesandproduces?
Good answer:
consumes describes what request body media type the endpoint accepts, usually based on the Content-Type header. For example, consumes = "application/json" means the request body must be JSON. produces describes what response media type the endpoint can return, usually based on the Accept header and content negotiation. For example, produces = "application/json" means the endpoint can return JSON.
70. Interview Answer
Question:
How do you design REST paths?
Good answer:
I use resource-oriented paths with nouns, not action verbs. For example, I use GET /api/tasks/123 instead of /api/getTask. The HTTP method describes the action. I use path variables for resource identity, such as task ID, and query parameters for filtering, sorting, and pagination.
71. Tiny Code Practice
Create this controller:
@RestController
@RequestMapping("/api/clients/{clientId}/tasks")
public class ClientTaskController {
@GetMapping
public List<TaskDto> list(
@PathVariable Long clientId,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestHeader(value = "X-Request-Id", required = false) String requestId
) {
return taskService.find(clientId, status, page, size, requestId);
}
@PostMapping(
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE
)
public ResponseEntity<TaskDto> create(
@PathVariable Long clientId,
@RequestBody CreateTaskRequest request
) {
TaskDto created = taskService.create(clientId, request);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}
Request:
GET /api/clients/10/tasks?status=OPEN&page=1&size=5
X-Request-Id: req-999
Questions:
- Which method handles the request?
- What is
clientId? - What is
status? - What is
page? - What is
size? - What is
requestId?
Answers:
list10OPEN15req-999
72. Tiny Bug Practice 1
Problem:
@GetMapping("/api/tasks")
public List<TaskDto> list1() {
return taskService.findAll();
}
@GetMapping("/api/tasks")
public List<TaskDto> list2() {
return taskService.findRecent();
}
Question:
What is wrong?
Answer:
Both methods have the same mapping. Spring cannot decide which method should handle GET /api/tasks, so the application can fail with an ambiguous mapping error.
73. Tiny Bug Practice 2
Problem:
@PostMapping(
value = "/api/tasks",
consumes = MediaType.APPLICATION_JSON_VALUE
)
public TaskDto create(@RequestBody CreateTaskRequest request) {
return taskService.create(request);
}
Request:
POST /api/tasks
Content-Type: text/plain
hello
Question:
What happens?
Answer:
The endpoint consumes JSON, but the request sends text/plain. Spring usually returns 415 Unsupported Media Type.
74. Tiny Bug Practice 3
Problem:
@GetMapping("/api/tasks")
public List<TaskDto> list(@RequestParam String status) {
return taskService.findByStatus(status);
}
Request:
GET /api/tasks
Question:
What happens?
Answer:
status is required by default. Since it is missing, Spring usually returns 400 Bad Request.
Fix:
@RequestParam(required = false) String status
or:
@RequestParam(defaultValue = "OPEN") String status
Practice Questions and Answers
Question 1
What does @RequestMapping do?
Answer:
@RequestMapping maps HTTP requests to controller classes or methods. It can match by path, HTTP method, query parameters, headers, consumed media type, and produced media type.
Question 2
Why should I prefer @GetMapping over @RequestMapping for GET endpoints?
Answer:
@GetMapping clearly maps only GET requests. @RequestMapping without a method can match multiple HTTP methods, which is less precise for REST endpoints.
Question 3
Name five shortcut mapping annotations.
Answer:
Five shortcut mapping annotations are:
@GetMapping
@PostMapping
@PutMapping
@PatchMapping
@DeleteMapping
Question 4
How do class-level and method-level mappings combine?
Answer:
The class-level path and method-level path combine to form the final endpoint path. For example, @RequestMapping("/api/tasks") plus @GetMapping("/{id}") becomes GET /api/tasks/{id}.
Question 5
What does @PathVariable read?
Answer:
@PathVariable reads values from the URL path.
Question 6
What does @RequestParam read?
Answer:
@RequestParam reads query parameters from the URL.
Question 7
Is @RequestParam required by default?
Answer:
Yes. @RequestParam is required by default.
Question 8
How can I make a request parameter optional?
Answer:
Use required = false, Optional<T>, or defaultValue.
Question 9
What does @RequestHeader read?
Answer:
@RequestHeader reads HTTP request headers.
Question 10
What does @RequestBody read?
Answer:
@RequestBody reads the HTTP request body and converts it into a Java object.
Question 11
Can one method normally have two @RequestBody parameters?
Answer:
Usually no. An HTTP request has one body, so a method should normally have one @RequestBody object.
Question 12
What does params = "status" mean?
Answer:
params = "status" means the method matches only if the request has a status query parameter.
Question 13
What is the difference between params and @RequestParam?
Answer:
params is a mapping condition that helps choose the controller method. @RequestParam binds the query parameter value to a Java method parameter.
Question 14
What does headers = "X-API-Version=2" mean?
Answer:
It means the method matches only if the request has header X-API-Version with value 2.
Question 15
What is the difference between headers and @RequestHeader?
Answer:
headers is a mapping condition that helps choose the method. @RequestHeader binds a header value to a Java method parameter.
Question 16
What does consumes mean?
Answer:
consumes describes what request body media type the endpoint accepts, based on the Content-Type header.
Question 17
What does produces mean?
Answer:
produces describes what response media type the endpoint can return, based on content negotiation and the Accept header.
Question 18
What is the difference between Content-Type and Accept?
Answer:
Content-Type describes the format of the request body. Accept describes the response format the client wants.
Question 19
What can cause 415 Unsupported Media Type?
Answer:
415 Unsupported Media Type can happen when the request Content-Type is not supported by the endpoint’s consumes condition.
Question 20
What can cause ambiguous mapping?
Answer:
Ambiguous mapping happens when two or more controller methods match the same request and Spring cannot decide which one to use.
Final Memory Sentences
@RequestMappingmaps HTTP requests to controller methods.- Prefer
@GetMapping,@PostMapping,@PutMapping,@PatchMapping, and@DeleteMappingfor REST APIs. - Class-level and method-level mappings combine.
@PathVariablereads from the URL path.@RequestParamreads from the query string.@RequestHeaderreads from request headers.@RequestBodyreads from the HTTP body.@RequestParamis required by default.paramsis a mapping condition.@RequestParambinds a value.headersis a mapping condition.@RequestHeaderbinds a value.consumeschecks requestContent-Type.producesdescribes response media type.Content-Typedescribes request body format.Acceptdescribes desired response format.- Wrong
Content-Typecan cause 415. - Unsupported
Acceptcan cause 406. - Identical mappings can cause ambiguous mapping errors.
- Use path variables for resource identity.
- Use query parameters for filtering, sorting, and pagination.