docs
Annotations and mechanisms for binding HTTP request data to controller method parameters including path variables, query parameters, headers, cookies, request bodies, and various request attributes.
Binds URI template variables from the request path to method parameters.
/**
* Annotation which indicates that a method parameter should be bound to a URI template
* variable. Supported for RequestMapping annotated handler methods.
*
* If the method parameter is Map<String, String> or MultiValueMap<String, String>
* then the map is populated with all path variable names and values.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the path variable to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the path variable is required.
* Defaults to true, leading to an exception if the variable is missing in the request.
* Switch this to false if you prefer a null value in case of a missing path variable.
*/
boolean required() default true;
}Usage Example:
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}
@GetMapping("/{userId}/orders/{orderId}")
public Order getUserOrder(
@PathVariable Long userId,
@PathVariable Long orderId) {
return orderService.findByUserAndId(userId, orderId);
}
// Optional path variable
@GetMapping({"/search", "/search/{query}"})
public List<User> search(@PathVariable(required = false) String query) {
return query != null ? userService.search(query) : userService.findAll();
}
// All path variables as Map
@GetMapping("/archive/{year}/{month}/{day}")
public List<Article> getArchive(@PathVariable Map<String, String> pathVars) {
int year = Integer.parseInt(pathVars.get("year"));
int month = Integer.parseInt(pathVars.get("month"));
int day = Integer.parseInt(pathVars.get("day"));
return articleService.findByDate(year, month, day);
}
}Binds request parameters (query parameters or form data) to method parameters.
/**
* Annotation which indicates that a method parameter should be bound to a web request parameter.
*
* Supported for annotated handler methods. If the method parameter type is Map and a request
* parameter name is specified, then the request parameter value is converted to a Map assuming
* an appropriate conversion strategy is available. If the method parameter is Map<String, String>
* or MultiValueMap<String, String> and a parameter name is not specified, then the map parameter
* is populated with all request parameter names and values.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request parameter to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the parameter is required.
* Defaults to true, leading to an exception if the parameter is missing in the request.
* Switch this to false if you prefer a null value if the parameter is not present.
* Alternatively, provide a {@link #defaultValue}, which implicitly sets this flag to false.
*/
boolean required() default true;
/**
* The default value to use as a fallback when the request parameter is not provided
* or has an empty value.
* Supplying a default value implicitly sets {@link #required} to false.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}Usage Example:
@RestController
@RequestMapping("/api/products")
public class ProductController {
// Required parameter: GET /api/products/search?name=laptop
@GetMapping("/search")
public List<Product> search(@RequestParam String name) {
return productService.searchByName(name);
}
// Optional parameter with default
@GetMapping
public List<Product> getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
return productService.findAll(page, size);
}
// Optional parameter
@GetMapping("/filter")
public List<Product> filter(
@RequestParam(required = false) String category,
@RequestParam(required = false) Double minPrice,
@RequestParam(required = false) Double maxPrice) {
return productService.filter(category, minPrice, maxPrice);
}
// All request parameters as Map
@GetMapping("/advanced-search")
public List<Product> advancedSearch(@RequestParam Map<String, String> params) {
return productService.dynamicSearch(params);
}
// Multiple values for same parameter
@GetMapping("/by-ids")
public List<Product> getByIds(@RequestParam List<Long> ids) {
return productService.findByIds(ids);
}
}Binds the HTTP request body to a method parameter, using HTTP message converters to deserialize the body content.
/**
* Annotation indicating a method parameter should be bound to the body of the web request.
* The body of the request is passed through an HttpMessageConverter to resolve the method
* argument depending on the content type of the request. Optionally, automatic validation
* can be applied by annotating the argument with @Valid.
*
* Supported for annotated handler methods.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
/**
* Whether body content is required.
* Default is true, leading to an exception thrown if there is no body content.
* Switch this to false if you prefer null to be passed when the body content is null.
*/
boolean required() default true;
}Usage Example:
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User created = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
// With validation
@PostMapping("/validated")
public ResponseEntity<User> createValidatedUser(@RequestBody @Valid User user) {
User created = userService.save(user);
return ResponseEntity.status(HttpStatus.CREATED).body(created);
}
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(
@PathVariable Long id,
@RequestBody User user) {
User updated = userService.update(id, user);
return ResponseEntity.ok(updated);
}
// Optional request body
@PostMapping("/partial")
public ResponseEntity<User> partialUpdate(
@RequestBody(required = false) Map<String, Object> updates) {
if (updates == null) {
return ResponseEntity.badRequest().build();
}
User updated = userService.applyUpdates(updates);
return ResponseEntity.ok(updated);
}
}Binds a request header to a method parameter.
/**
* Annotation which indicates that a method parameter should be bound to a web request header.
*
* Supported for annotated handler methods. If the method parameter is Map<String, String>,
* MultiValueMap<String, String>, or HttpHeaders then the map is populated with all header
* names and values.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request header to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the header is required.
* Defaults to true, leading to an exception if the header is missing.
* Switch this to false if you prefer a null value if the header is not present.
* Alternatively, provide a {@link #defaultValue}, which implicitly sets this flag to false.
*/
boolean required() default true;
/**
* The default value to use as a fallback.
* Supplying a default value implicitly sets {@link #required} to false.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}Usage Example:
@RestController
@RequestMapping("/api/secure")
public class SecureController {
@GetMapping("/data")
public ResponseEntity<Data> getData(@RequestHeader("Authorization") String authToken) {
if (!securityService.validateToken(authToken)) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
}
return ResponseEntity.ok(dataService.getData());
}
@GetMapping("/content")
public String getContent(
@RequestHeader("Accept-Language") String language,
@RequestHeader(value = "User-Agent", required = false) String userAgent) {
return contentService.getLocalizedContent(language, userAgent);
}
// With default value
@GetMapping("/version")
public ApiResponse getVersionedData(
@RequestHeader(value = "X-API-Version", defaultValue = "1") String version) {
return apiService.getDataForVersion(version);
}
// All headers as Map
@GetMapping("/debug")
public Map<String, String> debugHeaders(@RequestHeader Map<String, String> headers) {
return headers;
}
// All headers as HttpHeaders
@GetMapping("/headers")
public HttpHeaders getAllHeaders(@RequestHeader HttpHeaders headers) {
return headers;
}
}Binds a cookie value to a method parameter.
/**
* Annotation to indicate that a method parameter is bound to an HTTP cookie.
*
* The method parameter may be declared as type Cookie or as cookie value type
* (String, int, etc.).
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the cookie to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the cookie is required.
* Defaults to true, leading to an exception if the cookie is missing.
* Switch this to false if you prefer a null value if the cookie is not present.
* Alternatively, provide a {@link #defaultValue}, which implicitly sets this flag to false.
*/
boolean required() default true;
/**
* The default value to use as a fallback.
* Supplying a default value implicitly sets {@link #required} to false.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}Usage Example:
@RestController
@RequestMapping("/api/preferences")
public class PreferencesController {
@GetMapping
public UserPreferences getPreferences(@CookieValue("sessionId") String sessionId) {
return preferencesService.findBySession(sessionId);
}
// Optional cookie with default
@GetMapping("/theme")
public String getTheme(
@CookieValue(value = "theme", defaultValue = "light") String theme) {
return theme;
}
// Cookie as javax.servlet.http.Cookie object
@GetMapping("/cookie-details")
public CookieInfo getCookieDetails(@CookieValue("userId") Cookie cookie) {
return new CookieInfo(
cookie.getName(),
cookie.getValue(),
cookie.getMaxAge(),
cookie.getPath()
);
}
@GetMapping("/tracking")
public TrackingData getTracking(
@CookieValue(value = "trackingId", required = false) String trackingId) {
if (trackingId == null) {
trackingId = trackingService.createNewTrackingId();
}
return trackingService.getData(trackingId);
}
}Binds a method parameter or method return value to a named model attribute, exposed to a web view. Also supports data binding from request parameters to an object.
/**
* Annotation that binds a method parameter or method return value to a named model attribute,
* exposed to a web view. Supported for controller classes with @RequestMapping methods.
*
* WARNING: Data binding can lead to security issues by exposing parts of the object graph
* that are not meant to be exposed. It is recommended to use a specific form object that
* contains only the properties that need to be bound.
*/
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the model attribute to bind to.
* The default model attribute name is inferred from the declared attribute type
* (i.e. the method parameter type or method return type), based on the non-qualified
* class name.
*/
@AliasFor("value")
String name() default "";
/**
* Allows declaring data binding disabled directly on an @ModelAttribute method parameter
* or on the attribute returned from an @ModelAttribute method, both of which would prevent
* data binding for that attribute.
* By default this is set to true in which case data binding applies. Set this to false
* to disable data binding.
*/
boolean binding() default true;
}Usage Example:
@Controller
@RequestMapping("/users")
public class UserFormController {
// Pre-populate model attribute for all requests
@ModelAttribute("countries")
public List<Country> populateCountries() {
return countryService.findAll();
}
// Form display
@GetMapping("/new")
public String showForm(Model model) {
model.addAttribute("user", new User());
return "userForm";
}
// Form submission with data binding
@PostMapping
public String createUser(@ModelAttribute("user") @Valid User user,
BindingResult result) {
if (result.hasErrors()) {
return "userForm";
}
userService.save(user);
return "redirect:/users";
}
// Without validation
@PostMapping("/simple")
public String createSimpleUser(@ModelAttribute User user) {
userService.save(user);
return "redirect:/users";
}
// Disable data binding for specific attributes
@ModelAttribute("currentUser")
public User getCurrentUser() {
return securityService.getCurrentUser();
}
@PostMapping("/update-preferences")
public String updatePreferences(
@ModelAttribute(binding = false) User currentUser,
@ModelAttribute UserPreferences preferences) {
preferencesService.update(currentUser.getId(), preferences);
return "redirect:/preferences";
}
}Binds a method parameter to a session attribute.
/**
* Annotation to bind a method parameter to a session attribute.
*
* The main motivation is to provide convenient access to existing, permanent session
* attributes (e.g. user authentication object) with an optional/required check and
* a cast to the target method parameter type.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SessionAttribute {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the session attribute to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the session attribute is required.
* Defaults to true, leading to an exception if the attribute is missing.
* Switch this to false if you prefer a null value if the attribute is not present.
*/
boolean required() default true;
}Usage Example:
@Controller
@RequestMapping("/shopping")
public class ShoppingController {
@GetMapping("/cart")
public String viewCart(@SessionAttribute("cart") ShoppingCart cart, Model model) {
model.addAttribute("cart", cart);
return "cartView";
}
@PostMapping("/checkout")
public String checkout(
@SessionAttribute("cart") ShoppingCart cart,
@SessionAttribute("user") User user) {
orderService.createOrder(user, cart);
return "redirect:/orders/confirmation";
}
// Optional session attribute
@GetMapping("/welcome")
public String welcome(
@SessionAttribute(value = "user", required = false) User user,
Model model) {
if (user != null) {
model.addAttribute("welcomeMessage", "Welcome back, " + user.getName());
}
return "home";
}
}Binds a method parameter to a request attribute.
/**
* Annotation to bind a method parameter to a request attribute.
*
* The main motivation is to provide convenient access to request attributes from a
* controller method with an optional/required check and a cast to the target method
* parameter type.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestAttribute {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the request attribute to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the request attribute is required.
* Defaults to true, leading to an exception if the attribute is missing.
* Switch this to false if you prefer a null value if the attribute is not present.
*/
boolean required() default true;
}Usage Example:
@RestController
@RequestMapping("/api/data")
public class DataController {
// Access attribute set by filter or interceptor
@GetMapping("/secure")
public ResponseEntity<Data> getSecureData(
@RequestAttribute("authenticatedUser") User user) {
Data data = dataService.getDataForUser(user);
return ResponseEntity.ok(data);
}
@GetMapping("/metrics")
public ResponseEntity<Metrics> getMetrics(
@RequestAttribute("requestStartTime") Long startTime) {
long duration = System.currentTimeMillis() - startTime;
Metrics metrics = metricsService.getMetrics(duration);
return ResponseEntity.ok(metrics);
}
// Optional request attribute
@GetMapping("/optional")
public String handleOptional(
@RequestAttribute(value = "customAttribute", required = false) String attr) {
return attr != null ? "Found: " + attr : "Not found";
}
}Binds a method parameter to a name-value pair within a path segment (matrix variables).
/**
* Annotation which indicates that a method parameter should be bound to a name-value pair
* within a path segment. Supported for RequestMapping annotated handler methods.
*
* If the method parameter type is Map and a matrix variable name is specified, then the
* matrix variable value is assumed to be of type String[] and the map is populated with
* all comma-separated values for the given matrix variable name. If the method parameter
* is Map<String, String> or MultiValueMap<String, String> and a variable name is
* not specified, then the map is populated with all matrix variable names and values.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MatrixVariable {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the matrix variable.
*/
@AliasFor("value")
String name() default "";
/**
* The name of the URI path variable where the matrix variable is located,
* if necessary for disambiguation (e.g. a matrix variable with the same
* name present in more than one path segment).
*/
String pathVar() default ValueConstants.DEFAULT_NONE;
/**
* Whether the matrix variable is required.
* Default is true, leading to an exception if the variable is missing.
* Switch this to false if you prefer a null value in case of a missing matrix variable.
* Alternatively, provide a {@link #defaultValue}, which implicitly sets this flag to false.
*/
boolean required() default true;
/**
* The default value to use as a fallback.
* Supplying a default value implicitly sets {@link #required} to false.
*/
String defaultValue() default ValueConstants.DEFAULT_NONE;
}Usage Example:
@RestController
@RequestMapping("/api/owners")
public class OwnerController {
// GET /api/owners/42;name=John;age=30
@GetMapping("/{ownerId}")
public Owner getOwner(
@PathVariable Long ownerId,
@MatrixVariable String name,
@MatrixVariable int age) {
return ownerService.findByIdAndAttributes(ownerId, name, age);
}
// GET /api/owners/42/pets/21;name=Fluffy;type=cat
@GetMapping("/{ownerId}/pets/{petId}")
public Pet getPet(
@PathVariable Long ownerId,
@PathVariable Long petId,
@MatrixVariable(pathVar = "petId") String name,
@MatrixVariable(pathVar = "petId") String type) {
return petService.findByAttributes(ownerId, petId, name, type);
}
// Optional matrix variable with default
@GetMapping("/search/{query}")
public List<Owner> search(
@PathVariable String query,
@MatrixVariable(defaultValue = "10") int limit) {
return ownerService.search(query, limit);
}
// All matrix variables as Map
@GetMapping("/filter/{criteria}")
public List<Owner> filter(
@PathVariable String criteria,
@MatrixVariable Map<String, String> matrixVars) {
return ownerService.filterByCriteria(criteria, matrixVars);
}
}Binds a method parameter to a part in a multipart request, providing access to both the content and headers of the part.
/**
* Annotation that can be used to associate the part of a "multipart/form-data" request
* with a method argument.
*
* Supported method argument types include MultipartFile in conjunction with Spring's
* MultipartResolver abstraction, javax.servlet.http.Part in conjunction with Servlet 3.0
* multipart requests, or otherwise for any other method argument, the content of the part
* is passed through an HttpMessageConverter considering the 'Content-Type' header of the
* request part. This is analogous to what @RequestBody does to resolve an argument based
* on the content of a regular request.
*
* Note that @RequestParam annotation can also be used to associate the part of a
* "multipart/form-data" request with a method argument supporting the same method argument
* types. The main difference is that when the method argument is not a String or raw
* MultipartFile / Part, @RequestParam relies on type conversion via a registered Converter
* or PropertyEditor while @RequestPart relies on HttpMessageConverters considering the
* 'Content-Type' header of the request part. @RequestParam is likely to be used with
* name-value form fields while @RequestPart is likely to be used with parts containing
* more complex content (e.g. JSON, XML).
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestPart {
/**
* Alias for {@link #name}.
*/
@AliasFor("name")
String value() default "";
/**
* The name of the part in the multipart request to bind to.
*/
@AliasFor("value")
String name() default "";
/**
* Whether the part is required.
* Defaults to true, leading to an exception if the part is missing.
* Switch this to false if you prefer a null value if the part is not present.
*/
boolean required() default true;
}Usage Example:
@RestController
@RequestMapping("/api/upload")
public class UploadController {
// File upload with metadata
@PostMapping("/document")
public ResponseEntity<Document> uploadDocument(
@RequestPart("file") MultipartFile file,
@RequestPart("metadata") DocumentMetadata metadata) {
Document doc = documentService.save(file, metadata);
return ResponseEntity.status(HttpStatus.CREATED).body(doc);
}
// Multiple files with JSON data
@PostMapping("/gallery")
public ResponseEntity<Gallery> uploadGallery(
@RequestPart("images") List<MultipartFile> images,
@RequestPart("gallery") GalleryInfo info) {
Gallery gallery = galleryService.create(images, info);
return ResponseEntity.status(HttpStatus.CREATED).body(gallery);
}
// Optional file part
@PostMapping("/profile")
public ResponseEntity<Profile> updateProfile(
@RequestPart("profile") ProfileData profileData,
@RequestPart(value = "avatar", required = false) MultipartFile avatar) {
Profile profile = profileService.update(profileData, avatar);
return ResponseEntity.ok(profile);
}
// Using Part from Servlet 3.0
@PostMapping("/raw")
public ResponseEntity<String> uploadRaw(@RequestPart("data") Part part)
throws IOException {
String content = new String(part.getInputStream().readAllBytes());
return ResponseEntity.ok("Received: " + content);
}
}Constants for annotation default values.
/**
* Common value constants shared between annotation attributes.
*/
public final class ValueConstants {
/**
* Constant defining a value for no default - as a replacement for null which
* cannot be used in annotation attributes.
*/
public static final String DEFAULT_NONE = "\n\t\t\n\t\t\n\uE000\uE001\uE002\n\t\t\t\t\n";
private ValueConstants() {}
}Interface for a file uploaded in a multipart request (covered in detail in multipart.md).
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;
}