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

controller-annotations.mddocs/

Controller Annotations

Annotations for declaring web controllers and mapping HTTP requests to handler methods in Spring MVC.

Capabilities

@Controller

Marks a class as a Spring MVC controller capable of handling web requests.

/**
 * Indicates that an annotated class is a "Controller" (e.g. a web controller).
 *
 * This annotation serves as a specialization of @Component, allowing for
 * implementation classes to be autodetected through classpath scanning.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
    /**
     * The value may indicate a suggestion for a logical component name,
     * to be turned into a Spring bean in case of an autodetected component.
     */
    @AliasFor(annotation = Component.class)
    String value() default "";
}

Usage Example:

@Controller
public class HomeController {

    @GetMapping("/")
    public String home(Model model) {
        model.addAttribute("message", "Welcome");
        return "home";  // Returns view name
    }
}

@RestController

Convenience annotation that combines @Controller and @ResponseBody, indicating that all handler methods return response bodies rather than view names.

/**
 * A convenience annotation that is itself annotated with @Controller and @ResponseBody.
 *
 * Types that carry this annotation are treated as controllers where @RequestMapping
 * methods assume @ResponseBody semantics by default.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
    /**
     * The value may indicate a suggestion for a logical component name.
     */
    @AliasFor(annotation = Controller.class)
    String value() default "";
}

Usage Example:

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping
    public List<Product> getProducts() {
        return productService.findAll();  // Automatically serialized to JSON
    }

    @GetMapping("/{id}")
    public Product getProduct(@PathVariable Long id) {
        return productService.findById(id);
    }
}

@RequestMapping

Maps HTTP requests to handler classes and methods with flexible configuration of URL patterns, HTTP methods, request parameters, headers, and content types.

/**
 * Annotation for mapping web requests onto methods in request-handling classes
 * with flexible method signature.
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
    /**
     * Alias for {@link #path}.
     */
    @AliasFor("path")
    String[] value() default {};

    /**
     * The path mapping URIs (e.g. {@code "/profile"}).
     * Ant-style path patterns are also supported (e.g. {@code "/profile/**"}).
     * At the method level, relative paths (e.g. {@code "edit"}) are supported
     * within the primary mapping expressed at the type level.
     */
    @AliasFor("value")
    String[] path() default {};

    /**
     * The HTTP request methods to map to, narrowing the primary mapping:
     * GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE.
     */
    RequestMethod[] method() default {};

    /**
     * The parameters of the mapped request, narrowing the primary mapping.
     * Same format for any environment: a sequence of "myParam=myValue" style
     * expressions, with a request only mapped if each such parameter is found
     * to have the given value. Expressions can be negated by using the "!=" operator.
     */
    String[] params() default {};

    /**
     * The headers of the mapped request, narrowing the primary mapping.
     * Same format for any environment: a sequence of "My-Header=myValue" style
     * expressions, with a request only mapped if each such header is found
     * to have the given value. Expressions can be negated by using the "!=" operator.
     */
    String[] headers() default {};

    /**
     * Narrows the primary mapping by media types that can be consumed by the
     * mapped handler. Consists of one or more media types one of which must
     * match to the request Content-Type header.
     * Examples: "text/plain", "!text/plain" (negation), "application/json"
     */
    String[] consumes() default {};

    /**
     * Narrows the primary mapping by media types that can be produced by the
     * mapped handler. Consists of one or more media types one of which must
     * be acceptable according to the request "Accept" header.
     * Examples: "text/plain", "application/json"
     */
    String[] produces() default {};

    /**
     * Assign a name to this mapping.
     */
    String name() default "";
}

Usage Examples:

// Class-level mapping
@Controller
@RequestMapping("/api/orders")
public class OrderController {

    // GET /api/orders
    @RequestMapping(method = RequestMethod.GET)
    public List<Order> getOrders() {
        return orderService.findAll();
    }

    // POST /api/orders with JSON content type
    @RequestMapping(
        method = RequestMethod.POST,
        consumes = "application/json",
        produces = "application/json"
    )
    public Order createOrder(@RequestBody Order order) {
        return orderService.save(order);
    }

    // GET /api/orders/search?status=PENDING
    @RequestMapping(
        path = "/search",
        method = RequestMethod.GET,
        params = "status"
    )
    public List<Order> searchOrders(@RequestParam String status) {
        return orderService.findByStatus(status);
    }

    // Only handles requests with specific header
    @RequestMapping(
        path = "/admin",
        method = RequestMethod.GET,
        headers = "X-Admin-Token"
    )
    public List<Order> adminOrders(@RequestHeader("X-Admin-Token") String token) {
        return orderService.findAll();
    }
}

@GetMapping

Specialized @RequestMapping for HTTP GET requests.

/**
 * Annotation for mapping HTTP GET requests onto specific handler methods.
 * Specifically, @GetMapping is a composed annotation that acts as a
 * shortcut for @RequestMapping(method = RequestMethod.GET).
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.GET)
public @interface GetMapping {
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] consumes() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};

    @AliasFor(annotation = RequestMapping.class)
    String name() default "";
}

Usage Example:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping
    public List<User> getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findById(id);
    }

    @GetMapping(path = "/search", params = "email")
    public User findByEmail(@RequestParam String email) {
        return userService.findByEmail(email);
    }
}

@PostMapping

Specialized @RequestMapping for HTTP POST requests.

/**
 * Annotation for mapping HTTP POST requests onto specific handler methods.
 * Specifically, @PostMapping is a composed annotation that acts as a
 * shortcut for @RequestMapping(method = RequestMethod.POST).
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.POST)
public @interface PostMapping {
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] consumes() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};

    @AliasFor(annotation = RequestMapping.class)
    String name() default "";
}

Usage Example:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PostMapping
    public ResponseEntity<User> createUser(@RequestBody @Valid User user) {
        User created = userService.save(user);
        URI location = ServletUriComponentsBuilder
            .fromCurrentRequest()
            .path("/{id}")
            .buildAndExpand(created.getId())
            .toUri();
        return ResponseEntity.created(location).body(created);
    }

    @PostMapping(path = "/batch", consumes = "application/json")
    public List<User> createUsers(@RequestBody List<User> users) {
        return userService.saveAll(users);
    }
}

@PutMapping

Specialized @RequestMapping for HTTP PUT requests.

/**
 * Annotation for mapping HTTP PUT requests onto specific handler methods.
 * Specifically, @PutMapping is a composed annotation that acts as a
 * shortcut for @RequestMapping(method = RequestMethod.PUT).
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.PUT)
public @interface PutMapping {
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] consumes() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};

    @AliasFor(annotation = RequestMapping.class)
    String name() default "";
}

Usage Example:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PutMapping("/{id}")
    public ResponseEntity<User> updateUser(
            @PathVariable Long id,
            @RequestBody @Valid User user) {
        User updated = userService.update(id, user);
        return ResponseEntity.ok(updated);
    }
}

@PatchMapping

Specialized @RequestMapping for HTTP PATCH requests.

/**
 * Annotation for mapping HTTP PATCH requests onto specific handler methods.
 * Specifically, @PatchMapping is a composed annotation that acts as a
 * shortcut for @RequestMapping(method = RequestMethod.PATCH).
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.PATCH)
public @interface PatchMapping {
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] consumes() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};

    @AliasFor(annotation = RequestMapping.class)
    String name() default "";
}

Usage Example:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @PatchMapping("/{id}")
    public ResponseEntity<User> partialUpdateUser(
            @PathVariable Long id,
            @RequestBody Map<String, Object> updates) {
        User updated = userService.partialUpdate(id, updates);
        return ResponseEntity.ok(updated);
    }

    @PatchMapping("/{id}/status")
    public ResponseEntity<Void> updateStatus(
            @PathVariable Long id,
            @RequestParam String status) {
        userService.updateStatus(id, status);
        return ResponseEntity.noContent().build();
    }
}

@DeleteMapping

Specialized @RequestMapping for HTTP DELETE requests.

/**
 * Annotation for mapping HTTP DELETE requests onto specific handler methods.
 * Specifically, @DeleteMapping is a composed annotation that acts as a
 * shortcut for @RequestMapping(method = RequestMethod.DELETE).
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@RequestMapping(method = RequestMethod.DELETE)
public @interface DeleteMapping {
    @AliasFor(annotation = RequestMapping.class)
    String[] value() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] path() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] params() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] headers() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] consumes() default {};

    @AliasFor(annotation = RequestMapping.class)
    String[] produces() default {};

    @AliasFor(annotation = RequestMapping.class)
    String name() default "";
}

Usage Example:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }

    // Soft delete with query parameter
    @DeleteMapping(path = "/{id}", params = "soft=true")
    public ResponseEntity<Void> softDeleteUser(@PathVariable Long id) {
        userService.softDelete(id);
        return ResponseEntity.noContent().build();
    }
}

@ResponseBody

Indicates that the return value of a method should be bound to the response body, serialized using message converters.

/**
 * Annotation that indicates a method return value should be bound to the
 * web response body. Supported for annotated handler methods.
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}

Usage Example:

@Controller
@RequestMapping("/api/data")
public class DataController {

    // Returns JSON instead of view name
    @GetMapping("/json")
    @ResponseBody
    public Map<String, Object> getJsonData() {
        Map<String, Object> data = new HashMap<>();
        data.put("status", "success");
        data.put("timestamp", System.currentTimeMillis());
        return data;
    }

    // Returns view name (no @ResponseBody)
    @GetMapping("/page")
    public String getPage(Model model) {
        model.addAttribute("data", "some data");
        return "dataPage";
    }
}

@ResponseStatus

Marks a method or exception class with a status code and reason that should be returned.

/**
 * Marks a method or exception class with the status code and reason that
 * should be returned. The status code is applied to the HTTP response when
 * the handler method is invoked and overrides status information set by
 * other means, like ResponseEntity or "redirect:".
 */
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseStatus {
    /**
     * Alias for {@link #code}.
     */
    @AliasFor("code")
    HttpStatus value() default HttpStatus.INTERNAL_SERVER_ERROR;

    /**
     * The status code to use for the response.
     */
    @AliasFor("value")
    HttpStatus code() default HttpStatus.INTERNAL_SERVER_ERROR;

    /**
     * The reason to be used for the response.
     */
    String reason() default "";
}

Usage Examples:

@RestController
@RequestMapping("/api/resources")
public class ResourceController {

    // Returns 201 Created
    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Resource createResource(@RequestBody Resource resource) {
        return resourceService.save(resource);
    }

    // Returns 204 No Content
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteResource(@PathVariable Long id) {
        resourceService.delete(id);
    }
}

// Exception with status code
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "Resource not found")
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

Types

RequestMethod

Enumeration of HTTP request methods.

public enum RequestMethod {
    GET,
    HEAD,
    POST,
    PUT,
    PATCH,
    DELETE,
    OPTIONS,
    TRACE
}

HttpStatus

Enumeration of HTTP status codes (from Spring Framework's http module).

public enum HttpStatus {
    // 2xx Success
    OK(200, "OK"),
    CREATED(201, "Created"),
    ACCEPTED(202, "Accepted"),
    NO_CONTENT(204, "No Content"),

    // 3xx Redirection
    MOVED_PERMANENTLY(301, "Moved Permanently"),
    FOUND(302, "Found"),
    SEE_OTHER(303, "See Other"),
    NOT_MODIFIED(304, "Not Modified"),
    TEMPORARY_REDIRECT(307, "Temporary Redirect"),
    PERMANENT_REDIRECT(308, "Permanent Redirect"),

    // 4xx Client Error
    BAD_REQUEST(400, "Bad Request"),
    UNAUTHORIZED(401, "Unauthorized"),
    FORBIDDEN(403, "Forbidden"),
    NOT_FOUND(404, "Not Found"),
    METHOD_NOT_ALLOWED(405, "Method Not Allowed"),
    CONFLICT(409, "Conflict"),
    UNPROCESSABLE_ENTITY(422, "Unprocessable Entity"),

    // 5xx Server Error
    INTERNAL_SERVER_ERROR(500, "Internal Server Error"),
    NOT_IMPLEMENTED(501, "Not Implemented"),
    BAD_GATEWAY(502, "Bad Gateway"),
    SERVICE_UNAVAILABLE(503, "Service Unavailable"),
    GATEWAY_TIMEOUT(504, "Gateway Timeout");

    public int value();
    public String getReasonPhrase();
    public boolean is1xxInformational();
    public boolean is2xxSuccessful();
    public boolean is3xxRedirection();
    public boolean is4xxClientError();
    public boolean is5xxServerError();
}