or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcontrollers.mdembedded-server.mderror-handling.mdhttp-clients.mdindex.mdjson-processing.mdstatic-resources.mdtesting.md

controllers.mddocs/

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

```