> ## Documentation Index
> Fetch the complete documentation index at: https://suvera.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP Request Mapping with Winter Boot PHP 8 Attributes

> Route requests in Winter Boot using #[RequestMapping], method shortcuts #[GetMapping] and #[PostMapping], plus #[PathVariable] and #[RequestParam].

Winter Boot uses PHP 8 native attributes for routing. Instead of maintaining a central routes file, you place mapping attributes directly on controller methods — or on the controller class itself to establish a base URI prefix. The framework reads these attributes at startup, builds a routing table, and dispatches every incoming request to the correct handler with all parameters already bound and type-cast.

## `#[RequestMapping]`

`#[RequestMapping]` (namespace `dev\winterframework\stereotype\web\RequestMapping`) can be applied at **class level** to set a base URI prefix, or at **method level** to define the full route for that handler. When both are present, the class-level path is prepended to the method-level path automatically.

<ParamField body="path" type="string|array" required>
  The URI pattern for this mapping. Supports `{variable}` placeholders for path variables. Accepts a single string or an array of strings to map multiple paths to the same handler.
</ParamField>

<ParamField body="method" type="array">
  List of `RequestMethod` constants this handler accepts — for example, `[RequestMethod::GET, RequestMethod::POST]`. Defaults to all HTTP methods when omitted.
</ParamField>

<ParamField body="name" type="string">
  An optional human-readable name for this mapping. Useful for logging and debugging.
</ParamField>

<ParamField body="consumes" type="array">
  List of media types this handler consumes — for example, `['application/json']`. Requests with a non-matching `Content-Type` are rejected.
</ParamField>

<ParamField body="produces" type="array">
  List of media types this handler produces — for example, `['application/json']`. Used to negotiate the response `Content-Type`.
</ParamField>

### RequestMethod Enum Values

`RequestMethod` (namespace `dev\winterframework\enums\RequestMethod`) defines constants for every HTTP method the framework accepts in the `method` array.

| Constant                 | HTTP Method |
| ------------------------ | ----------- |
| `RequestMethod::GET`     | GET         |
| `RequestMethod::HEAD`    | HEAD        |
| `RequestMethod::POST`    | POST        |
| `RequestMethod::PUT`     | PUT         |
| `RequestMethod::PATCH`   | PATCH       |
| `RequestMethod::DELETE`  | DELETE      |
| `RequestMethod::OPTIONS` | OPTIONS     |
| `RequestMethod::TRACE`   | TRACE       |

### Class-level and Method-level Paths Combined

```php ProductController.php theme={null}
<?php

use dev\winterframework\stereotype\RestController;
use dev\winterframework\stereotype\web\RequestMapping;
use dev\winterframework\enums\RequestMethod;

#[RestController]
#[RequestMapping(path: '/api/v1')]          // base prefix for every method below
class ProductController
{
    // Handled at GET /api/v1/products
    #[RequestMapping(path: '/products', method: [RequestMethod::GET])]
    public function listProducts(): array
    {
        return [];
    }

    // Handled at POST /api/v1/products
    #[RequestMapping(path: '/products', method: [RequestMethod::POST])]
    public function createProduct(): array
    {
        return [];
    }
}
```

<Warning>
  Path variables — for example, `{id}` — are **not** allowed in a class-level `#[RequestMapping]`. They may only appear in method-level path definitions.
</Warning>

## HTTP Method Shortcuts

Winter Boot ships five convenience attributes that combine `#[RequestMapping]` with a fixed HTTP method. They accept the same `path`, `name`, `consumes`, and `produces` parameters, but you never need to specify `method` explicitly.

<CardGroup cols={3}>
  <Card title="#[GetMapping]" icon="arrow-down-to-line">Handles HTTP GET requests</Card>
  <Card title="#[PostMapping]" icon="arrow-up-from-line">Handles HTTP POST requests</Card>
  <Card title="#[PutMapping]" icon="pen">Handles HTTP PUT requests</Card>
  <Card title="#[DeleteMapping]" icon="trash">Handles HTTP DELETE requests</Card>
  <Card title="#[PatchMapping]" icon="pen-to-square">Handles HTTP PATCH requests</Card>
</CardGroup>

```php theme={null}
use dev\winterframework\stereotype\web\GetMapping;

#[GetMapping(path: '/users')]
public function listUsers(): array
{
    return $this->userService->findAll();
}
```

## `#[RequestParam]`

`#[RequestParam]` (namespace `dev\winterframework\stereotype\web\RequestParam`) binds a query-string value, POST field, cookie, or HTTP header to a method parameter. Apply it at the **parameter level**.

<ParamField body="name" type="string">
  Name of the incoming parameter. Defaults to the PHP variable name when omitted.
</ParamField>

<ParamField body="required" type="bool">
  Whether the parameter must be present in the request. Defaults to `true`. Set to `false` to make it optional.
</ParamField>

<ParamField body="defaultValue" type="mixed">
  Value used when the parameter is absent and `required` is `false`.
</ParamField>

<ParamField body="source" type="string">
  Where to read the value from. Defaults to `'request'`. See the source table below.
</ParamField>

### Source Values

| Value     | Reads from                                                |
| --------- | --------------------------------------------------------- |
| `request` | URL query string **or** POST body (checked in that order) |
| `get`     | URL query string only (`$_GET`)                           |
| `post`    | POST url-encoded / form body only (`$_POST`)              |
| `cookie`  | HTTP cookie (`$_COOKIE`)                                  |
| `header`  | HTTP request header                                       |

```php theme={null}
use dev\winterframework\stereotype\web\GetMapping;
use dev\winterframework\stereotype\web\PostMapping;
use dev\winterframework\stereotype\web\RequestParam;

// Query-string: GET /divide?a=10&b=2
#[GetMapping(path: '/divide')]
public function divide(
    #[RequestParam] int $a,
    #[RequestParam] int $b,
): float {
    return $a / $b;
}

// Optional parameter with a default value
#[GetMapping(path: '/greet')]
public function greet(
    #[RequestParam(required: false, defaultValue: 'World')] string $name,
): string {
    return 'Hello, ' . $name;
}

// Read from a specific HTTP header
#[PostMapping(path: '/orders')]
public function createOrder(
    #[RequestParam(name: 'X-Tenant-Id', source: 'header')] string $tenantId,
): array {
    return $this->orderService->create($tenantId);
}

// Read from a cookie
#[GetMapping(path: '/profile')]
public function profile(
    #[RequestParam(name: 'session_token', source: 'cookie')] string $token,
): array {
    return $this->sessionService->getProfile($token);
}
```

<Note>
  `#[RequestParam]` only accepts scalar PHP types (`string`, `int`, `float`, `bool`). For custom classes, use `#[RequestBody]` instead.
</Note>

## `#[PathVariable]`

`#[PathVariable]` (namespace `dev\winterframework\stereotype\web\PathVariable`) binds a URI template segment — the `{placeholder}` in the path string — to a method parameter. The placeholder name must match the PHP variable name, or you can specify it explicitly via the `name` option. Supported scalar types are `string`, `int`, `float`, and `bool`.

```php theme={null}
use dev\winterframework\stereotype\web\GetMapping;
use dev\winterframework\stereotype\web\PathVariable;

// GET /hello/Alice  →  "Hello, Alice"
#[GetMapping(path: '/hello/{name}')]
public function sayHello(#[PathVariable] string $name): string
{
    return 'Hello, ' . $name;
}

// GET /users/42  →  finds user with id 42
#[GetMapping(path: '/users/{id}')]
public function getUser(#[PathVariable] int $id): array
{
    return $this->userService->findById($id);
}
```

<Warning>
  Every `{placeholder}` that appears in the path **must** have a matching `#[PathVariable]` parameter, and every `#[PathVariable]` parameter must correspond to a `{placeholder}`. A mismatch raises an `InvalidSyntaxException` at startup.
</Warning>

## `#[RequestBody]`

`#[RequestBody]` (namespace `dev\winterframework\stereotype\web\RequestBody`) binds the entire HTTP request body to a method parameter. Winter Boot deserialises JSON, XML, URL-encoded form bodies, **and** multipart forms into the target class automatically. The parameter must be type-hinted with a concrete class, or `string` to receive the raw body. Union types are not supported.

<Tabs>
  <Tab title="Deserialised Object">
    ```php theme={null}
    use dev\winterframework\stereotype\web\PostMapping;
    use dev\winterframework\stereotype\web\RequestBody;

    // Accepts both JSON and url-encoded bodies:
    // {"a": 10, "b": 20}  or  a=10&b=20
    #[PostMapping(path: '/calc/add')]
    public function add(#[RequestBody] AddRequest $request): int
    {
        return $this->calcService->add($request->a, $request->b);
    }
    ```

    ```php AddRequest.php theme={null}
    class AddRequest
    {
        public int $a;
        public int $b;
    }
    ```
  </Tab>

  <Tab title="Raw String Body">
    ```php theme={null}
    #[PostMapping(path: '/webhooks/ingest')]
    public function ingest(#[RequestBody] string $rawPayload): array
    {
        $data = json_decode($rawPayload, true);
        return $this->webhookService->handle($data);
    }
    ```
  </Tab>
</Tabs>

<Note>
  Only one `#[RequestBody]` parameter is allowed per handler method. You can freely combine it with `#[PathVariable]` and `#[RequestParam]` parameters in the same method signature.
</Note>

### Single-string-arg constructor fallback

When the request `Content-Type` is none of form, multipart, JSON, or XML — or when parsing is disabled (see `disableParsing` below) — the raw body string is passed straight to your class constructor. The class must therefore declare a constructor taking a single string argument:

```php theme={null}
class CsvPayload
{
    public array $rows;

    public function __construct(string $raw)
    {
        $this->rows = explode("\n", trim($raw));
    }
}
```

Any other constructor shape fails here and the request is rejected with `400 Bad Request`.

<ParamField body="disableParsing" type="bool">
  Skips JSON/XML deserialisation. Defaults to `false`. When `true`, the raw body is handed to the single-string-arg constructor instead — useful for hand-rolled formats.
</ParamField>

```php theme={null}
use dev\winterframework\stereotype\web\PostMapping;
use dev\winterframework\stereotype\web\RequestBody;

#[PostMapping(path: '/import')]
public function import(#[RequestBody(disableParsing: true)] CsvPayload $payload): array
{
    return $this->importService->handle($payload->rows);
}
```

### File uploads (`multipart/form-data`)

For `multipart/form-data` requests, text fields and uploaded files are merged into a single map keyed by field name, then bound to your DTO by property name:

* Declare text fields as scalars (`string`, `int`, `float`, `bool`) — values are type-cast.
* Declare a file field as `HttpUploadedFile` (namespace `dev\winterframework\web\http\HttpUploadedFile`) to get a typed object — `getFilePath()` for the temp path, plus `getName()`, `getSize()`, `getError()` / `getErrorText()`. Declare it as `array` instead to receive the raw `$_FILES` entry (`name`, `type`, `tmp_name`, `error`, `size`).
* A multi-file field (`<input type="file" name="photos" multiple>`) keeps PHP's parallel-array `$_FILES` shape — normalise it yourself, or inject `HttpRequest` and use `getFiles()` / `getFile($name)` instead.

```php theme={null}
use dev\winterframework\stereotype\web\PostMapping;
use dev\winterframework\stereotype\web\RequestBody;
use dev\winterframework\web\http\HttpUploadedFile;

class AvatarUpload
{
    public string $username;
    public HttpUploadedFile $avatar;  // matches <input name="avatar" type="file">
    // public array $avatar;          // alternative: raw $_FILES entry
}

#[PostMapping(path: '/avatar')]
public function upload(#[RequestBody] AvatarUpload $req): array
{
    return ['tmp' => $req->avatar->getFilePath(), 'size' => $req->avatar->getSize()];
}
```

### Typed list properties

DTO properties may be typed as `StringList`, `IntegerList`, or `FloatList` (namespace `dev\winterframework\type`) to receive JSON arrays (or repeated `field[]` form params) as typed collections. Numbers are coerced to strings for `StringList`; whole-number strings are coerced to integers for `IntegerList` (floats such as `1.5`, `2.0`, or `"2.0"` are rejected); integers and numeric strings are coerced to floats for `FloatList`. Anything else is rejected with `400 Bad Request`.

```php theme={null}
use dev\winterframework\type\FloatList;
use dev\winterframework\type\IntegerList;
use dev\winterframework\type\StringList;

class BulkRequest
{
    public StringList $csvRow;   // ["a", "b"]  — or ["a", 1] → ["a", "1"]
    public IntegerList $ids;     // [1, "2"] → [1, 2]
    public FloatList $scores;    // [1.5, 2, "3.25"] → [1.5, 2.0, 3.25]
}
```
