or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api-versioning.mdasync.mdconfiguration.mdcontent-negotiation.mdcontroller-annotations.mdcore-framework.mdcors.mddata-binding.mdexception-handling.mdflash-attributes.mdfunctional-endpoints.mdi18n.mdindex.mdinterceptors.mdmultipart.mdrequest-binding.mdresource-handling.mdresponse-handling.mduri-building.mdview-resolution.md
tile.json

request-binding.mddocs/

Request Parameter Binding

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.

Capabilities

@PathVariable

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);
    }
}

@RequestParam

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&lt;String, String&gt;
 * or MultiValueMap&lt;String, String&gt; 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);
    }
}

@RequestBody

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);
    }
}

@RequestHeader

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&lt;String, String&gt;,
 * MultiValueMap&lt;String, String&gt;, 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;
    }
}

@CookieValue

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);
    }
}

@ModelAttribute

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";
    }
}

@SessionAttribute

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";
    }
}

@RequestAttribute

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";
    }
}

@MatrixVariable

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&lt;String, String&gt; or MultiValueMap&lt;String, String&gt; 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);
    }
}

@RequestPart

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);
    }
}

Types

ValueConstants

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() {}
}

MultipartFile

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;
}