Skip to content

Request

HTTP request has a method, URI, a set of headers and a body:

POST /contact HTTP/1.1
Host: example.org
Accept-Language: en-us
Accept-Encoding: gzip, deflate

{
    "subject": "Hello",
    "body": "Hello there, we need to build Yii application together!"
}

The method is POST, URI is /contact. Extra headers are specifying host, preferred language and encoding. The body could be anything. In this case, it's a JSON payload.

Yii uses PSR-7 ServerRequest as request representation. The object is available in controller actions and other types of middleware:

php
public function view(ServerRequestInterface $request): ResponseInterface
{
    // ...
}

Method

The method could be obtained from a request object:

php
$method = $request->getMethod();

Usually it's one of the:

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD
  • PATCH
  • OPTIONS

In case you want to make sure the request method is of a certain type, there is a special class with method names:

php
use Yiisoft\Http\Method;

if ($request->getMethod() === Method::POST) {
    // method is POST
}

URI

A URI has:

  • Scheme (http, https)
  • Host (yiiframework.com)
  • Port (80, 443)
  • Path (/posts/1)
  • Query string (page=1&sort=+id)
  • Fragment (#anchor)

You can obtain UriInterface from request like the following:

php
$uri = $request->getUri();

Then you can get various details from its methods:

  • getScheme()
  • getAuthority()
  • getUserInfo()
  • getHost()
  • getPort()
  • getPath()
  • getQuery()
  • getFragment()

Headers

There are various methods to inspect request headers. To get all headers as an array:

php
$headers = $request->getHeaders();
foreach ($headers as $name => $values) {
   // ...
}

To get a single header:

php
$values = $request->getHeader('Accept-Encoding');

Also, you could get value as a comma-separated string instead of an array. That's especially handy if a header has a single value:

php
if ($request->getHeaderLine('X-Requested-With') === 'XMLHttpRequest') {
    // This is an AJAX request made with jQuery.
    // Note that header presence and name may vary depending on the library used. 
}

To check if a header is present in the request:

php
if ($request->hasHeader('Accept-Encoding')) {
    // ...
}

Body

There are two methods to get body contents. The first is getting the body as it is without parsing:

php
$body = $request->getBody();

The $body would be an instance of Psr\Http\Message\StreamInterface.

Also, you could get a parsed body:

php
$bodyParameters = $request->getParsedBody();

Parsing depends on PSR-7 implementation and may require middleware for custom body formats.

php
<?php
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;

final readonly class JsonBodyParserMiddleware implements MiddlewareInterface
{
    public function process(Request $request, RequestHandler $next): Response
    {
        $contentType = $request->getHeaderLine('Content-Type');

        if (strpos($contentType, 'application/json') !== false) {
            $body = $request->getBody();
            $parsedBody = $this->parse($body);
            $request = $request->withParsedBody($parsedBody);
            
        }

        return $next->handle($request);
    }
}

File uploads

Uploaded files that user submitted from a form with enctype attribute equals to multipart/form-data are handled via special request method:

php
$files = $request->getUploadedFiles();
foreach ($files as $file) {
    if ($file->getError() === UPLOAD_ERR_OK) {
        $file->moveTo('path/to/uploads/new_filename.ext');
    }
}

Always validate the upload before reading the stream or moving the file. The file may be missing, too large for PHP configuration, only partially uploaded, rejected by a PHP extension, or not match the file type and size your application expects. Yii Validator provides the Yiisoft\Validator\Rule\File rule for this. It accepts PSR-7 UploadedFileInterface values and validates upload error codes, extension, MIME type, and size. Use Yiisoft\Validator\Rule\Each when a field contains several files.

The following example handles a single avatar upload. It validates the uploaded file with Yii Validator, stores the file outside the public directory, and generates its own file name instead of trusting the client-provided name.

php
<?php

declare(strict_types=1);

namespace App\Web\Profile;

use finfo;
use Psr\Http\Message\ResponseFactoryInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use RuntimeException;
use Yiisoft\Aliases\Aliases;
use Yiisoft\Http\Status;
use Yiisoft\Validator\Rule\File;
use Yiisoft\Validator\Rule\Image\Image;
use Yiisoft\Validator\Validator;

final readonly class UploadAvatarAction
{
    private const MAX_SIZE = 2 * 1024 * 1024;

    /**
     * @var array<string, string>
     */
    private const EXTENSIONS = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
    ];

    public function __construct(
        private Aliases $aliases,
        private ResponseFactoryInterface $responseFactory,
    ) {
    }

    public function __invoke(ServerRequestInterface $request): ResponseInterface
    {
        $file = $request->getUploadedFiles()['avatar'] ?? null;

        $result = (new Validator())->validate(
            ['avatar' => $file],
            [
                'avatar' => [
                    new File(
                        extensions: ['jpg', 'jpeg', 'png', 'webp'],
                        mimeTypes: array_keys(self::EXTENSIONS),
                        maxSize: self::MAX_SIZE,
                    ),
                    new Image(skipOnError: true),
                ],
            ],
        );

        if (!$result->isValid() || !$file instanceof UploadedFileInterface) {
            $messages = $result->getPropertyErrorMessages('avatar');
            return $this->badRequest(
                $messages === [] ? 'Upload a file in the "avatar" field.' : implode(' ', $messages),
            );
        }

        $uploadDirectory = $this->aliases->get('@runtime/uploads/avatars');
        $this->ensureDirectory($uploadDirectory);

        $temporaryPath = $uploadDirectory . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16)) . '.upload';
        $file->moveTo($temporaryPath);

        $mediaType = (new finfo(FILEINFO_MIME_TYPE))->file($temporaryPath);

        if (!is_string($mediaType) || !isset(self::EXTENSIONS[$mediaType])) {
            unlink($temporaryPath);
            return $this->badRequest('Only JPEG, PNG, and WebP images are allowed.');
        }

        $fileName = bin2hex(random_bytes(16)) . '.' . self::EXTENSIONS[$mediaType];
        $targetPath = $uploadDirectory . DIRECTORY_SEPARATOR . $fileName;

        if (!rename($temporaryPath, $targetPath)) {
            unlink($temporaryPath);
            throw new RuntimeException('Unable to store uploaded file.');
        }

        $response = $this->responseFactory->createResponse(Status::CREATED);
        $response->getBody()->write('Stored as ' . $fileName);

        return $response;
    }

    private function badRequest(string $message): ResponseInterface
    {
        $response = $this->responseFactory->createResponse(Status::BAD_REQUEST);
        $response->getBody()->write($message);

        return $response;
    }

    private function ensureDirectory(string $directory): void
    {
        if (!is_dir($directory) && !mkdir($directory, 0775, true) && !is_dir($directory)) {
            throw new RuntimeException(sprintf('Directory "%s" was not created.', $directory));
        }
    }
}

Notes:

  • Store uploads under @runtime or another non-public directory unless the files are intentionally public.
  • Do not use getClientFilename() as a storage path. A browser controls it, so it may contain unsafe characters, path-like values, or duplicate names.
  • Do not rely on getClientMediaType() alone. It comes from the client. Use File MIME type validation, Image for image validation and dimension checks, or another server-side parser appropriate for the file format.
  • Check PHP limits such as upload_max_filesize, post_max_size, and max_file_uploads when expected files are rejected before the action runs.
  • When a form uploads several files, the array returned by getUploadedFiles() has the same shape as the file input names. Validate them with Each(new File(...)) and move each UploadedFileInterface separately.
  • File can also validate string file paths and SplFileInfo instances. Do not pass user-submitted paths directly: use PSR-7 uploaded file objects from the request, or perform upload provenance checks before validating filesystem paths.

Attributes

Application middleware may set custom request attributes using withAttribute() method. You can get these attributes with getAttribute().

Route arguments, such as id from a /posts/{id} route pattern, aren't request attributes. Use #[RouteArgument] in an action parameter or inject Yiisoft\Router\CurrentRoute when you need route arguments or route metadata.