0
# Controller Development
1
2
RESTful web services and traditional MVC controllers using Spring annotations for web application development.
3
4
## Capabilities
5
6
### Controller Annotations
7
8
Mark classes and methods as web controllers with request mapping capabilities.
9
10
```java { .api }
11
/**
12
* Marks a class as a Spring MVC controller
13
*/
14
@Controller
15
public class WebController {
16
// Controller methods
17
}
18
19
/**
20
* Combination of @Controller and @ResponseBody for REST APIs
21
*/
22
@RestController
23
public class ApiController {
24
// REST endpoint methods
25
}
26
27
/**
28
* Global REST controller advice for exception handling and model attributes
29
*/
30
@RestControllerAdvice
31
public class GlobalRestControllerAdvice {
32
// Global REST exception handling
33
}
34
35
/**
36
* Maps HTTP requests to handler methods
37
*/
38
@RequestMapping(value = "/path", method = RequestMethod.GET)
39
public ResponseEntity<String> handleRequest();
40
41
/**
42
* HTTP method-specific mapping annotations
43
*/
44
@GetMapping("/users/{id}")
45
public User getUser(@PathVariable Long id);
46
47
@PostMapping("/users")
48
public User createUser(@RequestBody User user);
49
50
@PutMapping("/users/{id}")
51
public User updateUser(@PathVariable Long id, @RequestBody User user);
52
53
@DeleteMapping("/users/{id}")
54
public ResponseEntity<Void> deleteUser(@PathVariable Long id);
55
56
@PatchMapping("/users/{id}")
57
public User partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates);
58
```
59
60
**Usage Examples:**
61
62
```java
63
@RestController
64
@RequestMapping("/api/v1")
65
public class ProductController {
66
67
@GetMapping("/products")
68
public List<Product> getAllProducts(
69
@RequestParam(defaultValue = "0") int page,
70
@RequestParam(defaultValue = "10") int size) {
71
return productService.findAll(page, size);
72
}
73
74
@GetMapping("/products/{id}")
75
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
76
Product product = productService.findById(id);
77
return product != null ? ResponseEntity.ok(product) : ResponseEntity.notFound().build();
78
}
79
80
@PostMapping("/products")
81
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
82
Product created = productService.save(product);
83
return ResponseEntity.status(HttpStatus.CREATED).body(created);
84
}
85
}
86
```
87
88
### Parameter Binding
89
90
Bind HTTP request data to method parameters using annotations.
91
92
```java { .api }
93
/**
94
* Binds request parameters to method parameters
95
*/
96
@RequestParam(value = "name", required = false, defaultValue = "default") String paramName;
97
98
/**
99
* Binds URI template variables to method parameters
100
*/
101
@PathVariable("id") Long id;
102
@PathVariable Map<String, String> pathVars; // All path variables
103
104
/**
105
* Binds HTTP request body to method parameter
106
*/
107
@RequestBody User user;
108
109
/**
110
* Binds request headers to method parameters
111
*/
112
@RequestHeader("User-Agent") String userAgent;
113
@RequestHeader Map<String, String> headers; // All headers
114
115
/**
116
* Binds cookie values to method parameters
117
*/
118
@CookieValue("JSESSIONID") String sessionId;
119
120
/**
121
* Binds session attributes to method parameters
122
*/
123
@SessionAttribute("user") User currentUser;
124
125
/**
126
* Binds request attributes to method parameters
127
*/
128
@RequestAttribute("startTime") Long startTime;
129
130
/**
131
* Binds multipart files to method parameters
132
*/
133
@RequestParam("file") MultipartFile file;
134
@RequestParam("files") List<MultipartFile> files;
135
136
/**
137
* Binds specific parts of multipart requests
138
*/
139
@RequestPart("metadata") RequestMetadata metadata;
140
@RequestPart("file") MultipartFile file;
141
142
/**
143
* Binds form data to model objects
144
*/
145
@ModelAttribute User user;
146
@ModelAttribute("product") Product product;
147
148
/**
149
* Binds URI matrix variables to method parameters
150
*/
151
@MatrixVariable String action;
152
@MatrixVariable(pathVar="petId") int petId;
153
@MatrixVariable Map<String, String> matrixVars; // All matrix variables
154
```
155
156
**Usage Examples:**
157
158
```java
159
@RestController
160
public class FileController {
161
162
@PostMapping("/upload")
163
public ResponseEntity<String> handleFileUpload(
164
@RequestParam("file") MultipartFile file,
165
@RequestParam(value = "description", required = false) String description) {
166
167
if (file.isEmpty()) {
168
return ResponseEntity.badRequest().body("File is empty");
169
}
170
171
String filename = fileService.store(file, description);
172
return ResponseEntity.ok("File uploaded: " + filename);
173
}
174
175
@GetMapping("/search")
176
public List<Product> searchProducts(
177
@RequestParam String query,
178
@RequestParam(defaultValue = "name") String sortBy,
179
@RequestParam(defaultValue = "asc") String sortDir,
180
@RequestHeader(value = "Accept-Language", defaultValue = "en") String language) {
181
182
return productService.search(query, sortBy, sortDir, language);
183
}
184
}
185
```
186
187
### Response Handling
188
189
Control HTTP responses with status codes, headers, and content types.
190
191
```java { .api }
192
/**
193
* Marks method return value as response body
194
*/
195
@ResponseBody
196
public User getUser();
197
198
/**
199
* Sets HTTP response status
200
*/
201
@ResponseStatus(HttpStatus.CREATED)
202
public void createResource();
203
204
/**
205
* Generic HTTP response wrapper
206
*/
207
public class ResponseEntity<T> {
208
public static <T> ResponseEntity<T> ok(T body);
209
public static <T> ResponseEntity<T> ok().build();
210
public static <T> ResponseEntity<T> status(HttpStatus status);
211
public static <T> ResponseEntity<T> created(URI location);
212
public static <T> ResponseEntity<T> noContent().build();
213
public static <T> ResponseEntity<T> badRequest().build();
214
public static <T> ResponseEntity<T> notFound().build();
215
216
public ResponseEntity<T> header(String name, String value);
217
public ResponseEntity<T> headers(HttpHeaders headers);
218
}
219
220
/**
221
* HTTP status enumeration
222
*/
223
public enum HttpStatus {
224
// 2xx Success
225
OK(200), CREATED(201), ACCEPTED(202), NO_CONTENT(204),
226
// 4xx Client Error
227
BAD_REQUEST(400), UNAUTHORIZED(401), FORBIDDEN(403), NOT_FOUND(404),
228
// 5xx Server Error
229
INTERNAL_SERVER_ERROR(500), SERVICE_UNAVAILABLE(503);
230
}
231
```
232
233
**Usage Examples:**
234
235
```java
236
@RestController
237
public class OrderController {
238
239
@PostMapping("/orders")
240
public ResponseEntity<Order> createOrder(@Valid @RequestBody Order order) {
241
Order created = orderService.create(order);
242
URI location = ServletUriComponentsBuilder
243
.fromCurrentRequest()
244
.path("/{id}")
245
.buildAndExpand(created.getId())
246
.toUri();
247
return ResponseEntity.created(location).body(created);
248
}
249
250
@GetMapping("/orders/{id}")
251
public ResponseEntity<Order> getOrder(@PathVariable Long id) {
252
return orderService.findById(id)
253
.map(order -> ResponseEntity.ok()
254
.header("Last-Modified", order.getLastModified().toString())
255
.body(order))
256
.orElse(ResponseEntity.notFound().build());
257
}
258
259
@DeleteMapping("/orders/{id}")
260
public ResponseEntity<Void> deleteOrder(@PathVariable Long id) {
261
boolean deleted = orderService.deleteById(id);
262
return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build();
263
}
264
}
265
```
266
267
### Content Negotiation
268
269
Handle different request/response content types automatically.
270
271
```java { .api }
272
/**
273
* Specify consumable media types
274
*/
275
@PostMapping(value = "/data", consumes = "application/json")
276
public void handleJson(@RequestBody Data data);
277
278
@PostMapping(value = "/data", consumes = "application/xml")
279
public void handleXml(@RequestBody Data data);
280
281
/**
282
* Specify producible media types
283
*/
284
@GetMapping(value = "/data", produces = "application/json")
285
public Data getDataAsJson();
286
287
@GetMapping(value = "/data", produces = "application/xml")
288
public Data getDataAsXml();
289
290
/**
291
* Multiple content types
292
*/
293
@GetMapping(value = "/data", produces = {"application/json", "application/xml"})
294
public Data getData();
295
```
296
297
**Usage Examples:**
298
299
```java
300
@RestController
301
@RequestMapping("/api/reports")
302
public class ReportController {
303
304
@GetMapping(value = "/{id}", produces = {"application/json", "application/pdf"})
305
public ResponseEntity<?> getReport(
306
@PathVariable Long id,
307
@RequestHeader(value = "Accept", defaultValue = "application/json") String accept) {
308
309
Report report = reportService.findById(id);
310
311
if (accept.contains("application/pdf")) {
312
byte[] pdf = reportService.generatePdf(report);
313
return ResponseEntity.ok()
314
.contentType(MediaType.APPLICATION_PDF)
315
.body(pdf);
316
}
317
318
return ResponseEntity.ok(report);
319
}
320
321
@PostMapping(consumes = {"application/json", "application/xml"})
322
public ResponseEntity<Report> createReport(@RequestBody Report report) {
323
Report created = reportService.create(report);
324
return ResponseEntity.status(HttpStatus.CREATED).body(created);
325
}
326
}
327
```
328
329
### Bean Validation
330
331
Automatic validation of request data using JSR-303/349/380 Bean Validation annotations.
332
333
```java { .api }
334
/**
335
* Request body validation with @Valid
336
*/
337
@PostMapping("/users")
338
public ResponseEntity<?> createUser(@Valid @RequestBody User user, BindingResult result) {
339
if (result.hasErrors()) {
340
List<String> errors = result.getAllErrors().stream()
341
.map(DefaultMessageSourceResolvable::getDefaultMessage)
342
.collect(Collectors.toList());
343
return ResponseEntity.badRequest().body(errors);
344
}
345
return ResponseEntity.ok(userService.save(user));
346
}
347
348
/**
349
* Request parameter validation
350
*/
351
@GetMapping("/users")
352
public List<User> getUsers(
353
@RequestParam @Min(0) int page,
354
@RequestParam @Min(1) @Max(100) int size) {
355
return userService.findAll(page, size);
356
}
357
358
/**
359
* Path variable validation
360
*/
361
@GetMapping("/users/{id}")
362
public User getUser(@PathVariable @Min(1) Long id) {
363
return userService.findById(id);
364
}
365
366
/**
367
* Common validation annotations
368
*/
369
@NotNull(message = "Value cannot be null")
370
@NotBlank(message = "Value cannot be blank")
371
@Size(min = 2, max = 50, message = "Size must be between 2 and 50")
372
@Email(message = "Invalid email format")
373
@Pattern(regexp = "\\d{5}", message = "Must be 5 digits")
374
@Min(value = 0, message = "Must be non-negative")
375
@Max(value = 100, message = "Must not exceed 100")
376
@Past(message = "Date must be in the past")
377
@Future(message = "Date must be in the future")
378
```
379
380
**Usage Examples:**
381
382
```java
383
@RestController
384
@Validated // Enable method-level validation
385
public class ProductController {
386
387
@PostMapping("/products")
388
public ResponseEntity<?> createProduct(@Valid @RequestBody Product product, BindingResult result) {
389
if (result.hasErrors()) {
390
Map<String, String> errors = new HashMap<>();
391
result.getFieldErrors().forEach(error ->
392
errors.put(error.getField(), error.getDefaultMessage())
393
);
394
return ResponseEntity.badRequest().body(errors);
395
}
396
397
Product saved = productService.save(product);
398
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
399
}
400
401
@GetMapping("/products/search")
402
public List<Product> searchProducts(
403
@RequestParam @NotBlank @Size(min = 3) String query,
404
@RequestParam(defaultValue = "0") @Min(0) int page) {
405
return productService.search(query, page);
406
}
407
}
408
```
409
410
### Cross-Origin Resource Sharing (CORS)
411
412
Configure CORS support for cross-domain requests.
413
414
```java { .api }
415
/**
416
* CORS configuration at class or method level
417
*/
418
@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
419
@RestController
420
public class ApiController {
421
422
@CrossOrigin(origins = "*", allowedHeaders = "*")
423
@GetMapping("/public-data")
424
public Data getPublicData() {
425
return dataService.getPublicData();
426
}
427
}
428
429
/**
430
* Global CORS configuration
431
*/
432
@Configuration
433
public class WebConfig implements WebMvcConfigurer {
434
435
@Override
436
public void addCorsMappings(CorsRegistry registry) {
437
registry.addMapping("/api/**")
438
.allowedOrigins("http://localhost:3000", "https://mydomain.com")
439
.allowedMethods("GET", "POST", "PUT", "DELETE")
440
.allowedHeaders("*")
441
.allowCredentials(true)
442
.maxAge(3600);
443
}
444
}
445
```
446
447
### Data Binding and Validation
448
449
Configure data binding behavior and validation rules.
450
451
```java { .api }
452
/**
453
* Initialize data binding for specific controllers
454
*/
455
@InitBinder
456
public void initBinder(WebDataBinder binder) {
457
binder.setRequiredFields("name", "email");
458
binder.setAllowedFields("name", "email", "age");
459
binder.setDisallowedFields("id", "createdDate");
460
461
// Custom validator
462
binder.addValidators(new UserValidator());
463
}
464
465
/**
466
* Controller with session attributes
467
*/
468
@Controller
469
@SessionAttributes({"user", "preferences"})
470
public class SessionController {
471
472
@ModelAttribute("user")
473
public User initUser() {
474
return new User();
475
}
476
477
@PostMapping("/update-profile")
478
public String updateProfile(@ModelAttribute("user") User user, Model model) {
479
userService.update(user);
480
return "redirect:/profile";
481
}
482
}
483
```
484
485
## Types
486
487
```java { .api }
488
// Multipart file handling
489
public interface MultipartFile {
490
String getName();
491
String getOriginalFilename();
492
String getContentType();
493
boolean isEmpty();
494
long getSize();
495
byte[] getBytes() throws IOException;
496
InputStream getInputStream() throws IOException;
497
void transferTo(File dest) throws IOException;
498
}
499
500
// HTTP headers utility
501
public class HttpHeaders {
502
public static final String ACCEPT = "Accept";
503
public static final String CONTENT_TYPE = "Content-Type";
504
public static final String AUTHORIZATION = "Authorization";
505
506
public void add(String headerName, String headerValue);
507
public void set(String headerName, String headerValue);
508
public String getFirst(String headerName);
509
public List<String> get(String headerName);
510
}
511
512
// URI builder utility
513
public class ServletUriComponentsBuilder {
514
public static ServletUriComponentsBuilder fromCurrentRequest();
515
public ServletUriComponentsBuilder path(String path);
516
public UriComponents buildAndExpand(Object... uriVariableValues);
517
}
518
519
// Model and session attribute handling
520
public interface Model {
521
Model addAttribute(String attributeName, Object attributeValue);
522
Model addAttribute(Object attributeValue);
523
Map<String, Object> asMap();
524
boolean containsAttribute(String attributeName);
525
}
526
527
// Data binding and validation
528
public class WebDataBinder {
529
public void setValidator(Validator validator);
530
public void setRequiredFields(String... requiredFields);
531
public void setAllowedFields(String... allowedFields);
532
public void setDisallowedFields(String... disallowedFields);
533
}
534
535
// Session attribute configuration
536
@Target({ElementType.TYPE})
537
@Retention(RetentionPolicy.RUNTIME)
538
public @interface SessionAttributes {
539
String[] value() default {};
540
Class<?>[] types() default {};
541
}
542
543
// Cross-origin resource sharing configuration
544
@Target({ElementType.TYPE, ElementType.METHOD})
545
@Retention(RetentionPolicy.RUNTIME)
546
public @interface CrossOrigin {
547
String[] value() default {};
548
String[] origins() default {};
549
String[] allowedHeaders() default {};
550
String[] exposedHeaders() default {};
551
RequestMethod[] methods() default {};
552
String allowCredentials() default "";
553
long maxAge() default -1;
554
}
555
556
// Data binding customization
557
@Target({ElementType.METHOD})
558
@Retention(RetentionPolicy.RUNTIME)
559
public @interface InitBinder {
560
String[] value() default {};
561
}
562
563
// Bean Validation types
564
public interface BindingResult {
565
boolean hasErrors();
566
boolean hasGlobalErrors();
567
boolean hasFieldErrors();
568
List<ObjectError> getAllErrors();
569
List<FieldError> getFieldErrors();
570
FieldError getFieldError(String field);
571
}
572
573
public class FieldError extends ObjectError {
574
public String getField();
575
public Object getRejectedValue();
576
public String getDefaultMessage();
577
}
578
579
// Validation annotations
580
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
581
@Retention(RetentionPolicy.RUNTIME)
582
public @interface Valid {
583
}
584
585
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
586
@Retention(RetentionPolicy.RUNTIME)
587
public @interface Validated {
588
Class<?>[] value() default {};
589
}
590
591
// Common validation constraint annotations
592
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
593
@Retention(RetentionPolicy.RUNTIME)
594
public @interface NotNull {
595
String message() default "";
596
}
597
598
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
599
@Retention(RetentionPolicy.RUNTIME)
600
public @interface NotBlank {
601
String message() default "";
602
}
603
604
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
605
@Retention(RetentionPolicy.RUNTIME)
606
public @interface Size {
607
int min() default 0;
608
int max() default Integer.MAX_VALUE;
609
String message() default "";
610
}
611
612
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
613
@Retention(RetentionPolicy.RUNTIME)
614
public @interface Email {
615
String message() default "";
616
}
617
```