Starter for building web, including RESTful, applications using Spring MVC with embedded Tomcat server.
—
RESTful web services and traditional MVC controllers using Spring annotations for web application development.
Mark classes and methods as web controllers with request mapping capabilities.
/**
* Marks a class as a Spring MVC controller
*/
@Controller
public class WebController {
// Controller methods
}
/**
* Combination of @Controller and @ResponseBody for REST APIs
*/
@RestController
public class ApiController {
// REST endpoint methods
}
/**
* Global REST controller advice for exception handling and model attributes
*/
@RestControllerAdvice
public class GlobalRestControllerAdvice {
// Global REST exception handling
}
/**
* Maps HTTP requests to handler methods
*/
@RequestMapping(value = "/path", method = RequestMethod.GET)
public ResponseEntity<String> handleRequest();
/**
* HTTP method-specific mapping annotations
*/
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id);
@PostMapping("/users")
public User createUser(@RequestBody User user);
@PutMapping("/users/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user);
@DeleteMapping("/users/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id);
@PatchMapping("/users/{id}")
public User partialUpdate(@PathVariable Long id, @RequestBody Map<String, Object> updates);Usage Examples:
@RestController
@RequestMapping("/api/v1")
public class ProductController {
@GetMapping("/products")
public List<Product> getAllProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return productService.findAll(page, size);
}
@GetMapping("/products/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
Product product = productService.findById(id);
return product != null ? ResponseEntity.ok(product) : ResponseEntity.notFound().build();
}
@PostMapping("/products")
public ResponseEntity<Product> createProduct(@Valid @RequestBody Product product) {
Product created = productService.save(product);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}Bind HTTP request data to method parameters using annotations.
/**
* Binds request parameters to method parameters
*/
@RequestParam(value = "name", required = false, defaultValue = "default") String paramName;
/**
* Binds URI template variables to method parameters
*/
@PathVariable("id") Long id;
@PathVariable Map<String, String> pathVars; // All path variables
/**
* Binds HTTP request body to method parameter
*/
@RequestBody User user;
/**
* Binds request headers to method parameters
*/
@RequestHeader("User-Agent") String userAgent;
@RequestHeader Map<String, String> headers; // All headers
/**
* Binds cookie values to method parameters
*/
@CookieValue("JSESSIONID") String sessionId;
/**
* Binds session attributes to method parameters
*/
@SessionAttribute("user") User currentUser;
/**
* Binds request attributes to method parameters
*/
@RequestAttribute("startTime") Long startTime;
/**
* Binds multipart files to method parameters
*/
@RequestParam("file") MultipartFile file;
@RequestParam("files") List<MultipartFile> files;
/**
* Binds specific parts of multipart requests
*/
@RequestPart("metadata") RequestMetadata metadata;
@RequestPart("file") MultipartFile file;
/**
* Binds form data to model objects
*/
@ModelAttribute User user;
@ModelAttribute("product") Product product;
/**
* Binds URI matrix variables to method parameters
*/
@MatrixVariable String action;
@MatrixVariable(pathVar="petId") int petId;
@MatrixVariable Map<String, String> matrixVars; // All matrix variablesUsage Examples:
@RestController
public class FileController {
@PostMapping("/upload")
public ResponseEntity<String> handleFileUpload(
@RequestParam("file") MultipartFile file,
@RequestParam(value = "description", required = false) String description) {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("File is empty");
}
String filename = fileService.store(file, description);
return ResponseEntity.ok("File uploaded: " + filename);
}
@GetMapping("/search")
public List<Product> searchProducts(
@RequestParam String query,
@RequestParam(defaultValue = "name") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir,
@RequestHeader(value = "Accept-Language", defaultValue = "en") String language) {
return productService.search(query, sortBy, sortDir, language);
}
}Control HTTP responses with status codes, headers, and content types.
/**
* Marks method return value as response body
*/
@ResponseBody
public User getUser();
/**
* Sets HTTP response status
*/
@ResponseStatus(HttpStatus.CREATED)
public void createResource();
/**
* Generic HTTP response wrapper
*/
public class ResponseEntity<T> {
public static <T> ResponseEntity<T> ok(T body);
public static <T> ResponseEntity<T> ok().build();
public static <T> ResponseEntity<T> status(HttpStatus status);
public static <T> ResponseEntity<T> created(URI location);
public static <T> ResponseEntity<T> noContent().build();
public static <T> ResponseEntity<T> badRequest().build();
public static <T> ResponseEntity<T> notFound().build();
public ResponseEntity<T> header(String name, String value);
public ResponseEntity<T> headers(HttpHeaders headers);
}
/**
* HTTP status enumeration
*/
public enum HttpStatus {
// 2xx Success
OK(200), CREATED(201), ACCEPTED(202), NO_CONTENT(204),
// 4xx Client Error
BAD_REQUEST(400), UNAUTHORIZED(401), FORBIDDEN(403), NOT_FOUND(404),
// 5xx Server Error
INTERNAL_SERVER_ERROR(500), SERVICE_UNAVAILABLE(503);
}Usage Examples:
@RestController
public class OrderController {
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@Valid @RequestBody Order order) {
Order created = orderService.create(order);
URI location = ServletUriComponentsBuilder
.fromCurrentRequest()
.path("/{id}")
.buildAndExpand(created.getId())
.toUri();
return ResponseEntity.created(location).body(created);
}
@GetMapping("/orders/{id}")
public ResponseEntity<Order> getOrder(@PathVariable Long id) {
return orderService.findById(id)
.map(order -> ResponseEntity.ok()
.header("Last-Modified", order.getLastModified().toString())
.body(order))
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/orders/{id}")
public ResponseEntity<Void> deleteOrder(@PathVariable Long id) {
boolean deleted = orderService.deleteById(id);
return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build();
}
}Handle different request/response content types automatically.
/**
* Specify consumable media types
*/
@PostMapping(value = "/data", consumes = "application/json")
public void handleJson(@RequestBody Data data);
@PostMapping(value = "/data", consumes = "application/xml")
public void handleXml(@RequestBody Data data);
/**
* Specify producible media types
*/
@GetMapping(value = "/data", produces = "application/json")
public Data getDataAsJson();
@GetMapping(value = "/data", produces = "application/xml")
public Data getDataAsXml();
/**
* Multiple content types
*/
@GetMapping(value = "/data", produces = {"application/json", "application/xml"})
public Data getData();Usage Examples:
@RestController
@RequestMapping("/api/reports")
public class ReportController {
@GetMapping(value = "/{id}", produces = {"application/json", "application/pdf"})
public ResponseEntity<?> getReport(
@PathVariable Long id,
@RequestHeader(value = "Accept", defaultValue = "application/json") String accept) {
Report report = reportService.findById(id);
if (accept.contains("application/pdf")) {
byte[] pdf = reportService.generatePdf(report);
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_PDF)
.body(pdf);
}
return ResponseEntity.ok(report);
}
@PostMapping(consumes = {"application/json", "application/xml"})
public ResponseEntity<Report> createReport(@RequestBody Report report) {
Report created = reportService.create(report);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
}Automatic validation of request data using JSR-303/349/380 Bean Validation annotations.
/**
* Request body validation with @Valid
*/
@PostMapping("/users")
public ResponseEntity<?> createUser(@Valid @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
List<String> errors = result.getAllErrors().stream()
.map(DefaultMessageSourceResolvable::getDefaultMessage)
.collect(Collectors.toList());
return ResponseEntity.badRequest().body(errors);
}
return ResponseEntity.ok(userService.save(user));
}
/**
* Request parameter validation
*/
@GetMapping("/users")
public List<User> getUsers(
@RequestParam @Min(0) int page,
@RequestParam @Min(1) @Max(100) int size) {
return userService.findAll(page, size);
}
/**
* Path variable validation
*/
@GetMapping("/users/{id}")
public User getUser(@PathVariable @Min(1) Long id) {
return userService.findById(id);
}
/**
* Common validation annotations
*/
@NotNull(message = "Value cannot be null")
@NotBlank(message = "Value cannot be blank")
@Size(min = 2, max = 50, message = "Size must be between 2 and 50")
@Email(message = "Invalid email format")
@Pattern(regexp = "\\d{5}", message = "Must be 5 digits")
@Min(value = 0, message = "Must be non-negative")
@Max(value = 100, message = "Must not exceed 100")
@Past(message = "Date must be in the past")
@Future(message = "Date must be in the future")Usage Examples:
@RestController
@Validated // Enable method-level validation
public class ProductController {
@PostMapping("/products")
public ResponseEntity<?> createProduct(@Valid @RequestBody Product product, BindingResult result) {
if (result.hasErrors()) {
Map<String, String> errors = new HashMap<>();
result.getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.badRequest().body(errors);
}
Product saved = productService.save(product);
return ResponseEntity.status(HttpStatus.CREATED).body(saved);
}
@GetMapping("/products/search")
public List<Product> searchProducts(
@RequestParam @NotBlank @Size(min = 3) String query,
@RequestParam(defaultValue = "0") @Min(0) int page) {
return productService.search(query, page);
}
}Configure CORS support for cross-domain requests.
/**
* CORS configuration at class or method level
*/
@CrossOrigin(origins = "http://localhost:3000", maxAge = 3600)
@RestController
public class ApiController {
@CrossOrigin(origins = "*", allowedHeaders = "*")
@GetMapping("/public-data")
public Data getPublicData() {
return dataService.getPublicData();
}
}
/**
* Global CORS configuration
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000", "https://mydomain.com")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}Configure data binding behavior and validation rules.
/**
* Initialize data binding for specific controllers
*/
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setRequiredFields("name", "email");
binder.setAllowedFields("name", "email", "age");
binder.setDisallowedFields("id", "createdDate");
// Custom validator
binder.addValidators(new UserValidator());
}
/**
* Controller with session attributes
*/
@Controller
@SessionAttributes({"user", "preferences"})
public class SessionController {
@ModelAttribute("user")
public User initUser() {
return new User();
}
@PostMapping("/update-profile")
public String updateProfile(@ModelAttribute("user") User user, Model model) {
userService.update(user);
return "redirect:/profile";
}
}// Multipart file handling
public interface MultipartFile {
String getName();
String getOriginalFilename();
String getContentType();
boolean isEmpty();
long getSize();
byte[] getBytes() throws IOException;
InputStream getInputStream() throws IOException;
void transferTo(File dest) throws IOException;
}
// HTTP headers utility
public class HttpHeaders {
public static final String ACCEPT = "Accept";
public static final String CONTENT_TYPE = "Content-Type";
public static final String AUTHORIZATION = "Authorization";
public void add(String headerName, String headerValue);
public void set(String headerName, String headerValue);
public String getFirst(String headerName);
public List<String> get(String headerName);
}
// URI builder utility
public class ServletUriComponentsBuilder {
public static ServletUriComponentsBuilder fromCurrentRequest();
public ServletUriComponentsBuilder path(String path);
public UriComponents buildAndExpand(Object... uriVariableValues);
}
// Model and session attribute handling
public interface Model {
Model addAttribute(String attributeName, Object attributeValue);
Model addAttribute(Object attributeValue);
Map<String, Object> asMap();
boolean containsAttribute(String attributeName);
}
// Data binding and validation
public class WebDataBinder {
public void setValidator(Validator validator);
public void setRequiredFields(String... requiredFields);
public void setAllowedFields(String... allowedFields);
public void setDisallowedFields(String... disallowedFields);
}
// Session attribute configuration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SessionAttributes {
String[] value() default {};
Class<?>[] types() default {};
}
// Cross-origin resource sharing configuration
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface CrossOrigin {
String[] value() default {};
String[] origins() default {};
String[] allowedHeaders() default {};
String[] exposedHeaders() default {};
RequestMethod[] methods() default {};
String allowCredentials() default "";
long maxAge() default -1;
}
// Data binding customization
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface InitBinder {
String[] value() default {};
}
// Bean Validation types
public interface BindingResult {
boolean hasErrors();
boolean hasGlobalErrors();
boolean hasFieldErrors();
List<ObjectError> getAllErrors();
List<FieldError> getFieldErrors();
FieldError getFieldError(String field);
}
public class FieldError extends ObjectError {
public String getField();
public Object getRejectedValue();
public String getDefaultMessage();
}
// Validation annotations
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Valid {
}
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Validated {
Class<?>[] value() default {};
}
// Common validation constraint annotations
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNull {
String message() default "";
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotBlank {
String message() default "";
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Size {
int min() default 0;
int max() default Integer.MAX_VALUE;
String message() default "";
}
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface Email {
String message() default "";
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-boot--spring-boot-starter-web