Объекты запроса и ответа (Request & Response Objects)



Объекты запроса и ответа (Request & Response Objects) являются абстрактными представителями HTTP-запросов и ответов. Объект запроса в CakePHP позволяет работать с входящим запросом, в то время как объект ответа позволяет легко создавать ответы HTTP от контроллеров приложения.

Запрос (Request)

class Cake\Http\ServerRequest

ServerRequest - это объект запроса, используемый в CakePHP по умолчанию. Он централизует ряд функций для опроса и взаимодействия с данными запроса. По каждому запросу создается один такой объект, который затем передается по ссылке на различные уровни приложения, использующего данные запроса. По умолчанию запрос присваивается $this->request и доступен в Контроллерах (Controllers), Ячейках (Cells), Представлениях (Views) и Помощниках (Helpers). Вы также можете получить доступ к нему в компонентах (Components) контроллеровв, используя ссылку на контроллер. Некоторые из обязанностей ServerRequest включают в себя:

  • Обработку массивов GET, POST и FILES в структурах данных;
  • Обеспечение интроспекции среды, относящейся к запросу. Сюда входит такая информация, как отправленные заголовки, IP-адрес клиента и субдомен/домен сервера, на котором запущено приложение;
  • Предоставление доступа к параметрам запроса как индексов массивов, так и свойств объекта.

Начиная с версии 3.4.0, объект запроса CakePHP реализует PSR-7 ServerRequestInterface, что упрощает использование библиотек вне CakePHP.

Параметры запроса

Параметры маршрутизации из объекта запроса можно получить используя метод getParam():

$controllerName = $this->request->getParam('controller');

//До версии 3.4.0
$controllerName = $this->request->param('controller');

Через этот интерфейс доступны все элементы маршрута.

Помимо Route Elements (элементов маршрута),также часто необходим доступ к Passed Arguments (передаваемым аргументам). Они также доступны в объекте запроса:

//Passed arguments (передаваемым аргументам)
$passedArgs = $this->request->getParam('pass');

Всё это предоставят доступ к переданным аргументам. Существует несколько важных/полезных параметров, которые CakePHP использует внутренне, все они также находятся в параметрах маршрутизации:

  • plugin - Плагин, который обрабатывает текущий запрос. Будет null, если нет плагина;
  • controller - Контроллер, который обрабатывает текущий запрос;
  • action - Экшен, который обрабатывает текущий запрос;
  • prefix - Префикс для текущего действия (экшена). См. «Использование префиксов в маршрутизации» для получения дополнительной информации.

Параметры строки запроса

Cake\Http\ServerRequest::getQuery($name)

Параметры строки запроса можно прочитать с помощью метода getQuery():

// Для URL: /posts/index?page=1&sort=title
$page = $this->request->getQuery('page');

// До версии 3.4.0
$page = $this->request->query('page');

Вы можете либо напрямую получить доступ к свойству запроса, указав имя свойства, либо использовать метод getQuery() без указания имени (ключа) для чтения всего массива запроса. Любые ключи, которые не существуют, будут приводить к возвращению значения null:

$foo = $this->request->getQuery('value_that_does_not_exist');
// $foo === null

// Можно, так же, указать значения по умолчанию
$foo = $this->request->getQuery('does_not_exist', 'default val');

Так же, для получения доступа ко всем параметрам запроса, можно использовать getQueryParams ():

$query = $this->request->getQueryParams();

Новое в версии 3.4.0: getQueryParams() и getQuery() добавлены в 3.4.0

Данные тела запроса

Cake\Http\ServerRequest::getData($name, $default = null)

Доступ ко всем данным POST можно получить с помощью Cake\Http\ServerRequest::getData(). Любые данные, отправленные из формы, содержащие многомерный массив, будут иметь префикс данных. Например:

// Если на входе получены данные вида 'MyModel[title]', то доступ будет выглядеть следующим образом:
$title = $this->request->getData('MyModel.title');

Любые ключи, которые не существуют, будут возвращать значение null:

$foo = $this->request->getData('Value.that.does.not.exist');
// $foo == null

Данные методов PUT, PATCH или DELETE в запросе

Cake\Http\ServerRequest::input($callback[, $options])

При создании REST сервиса часто бывает необходимо принимать в запросе данные PUT и DELETE. Любые application/x-www-form-urlencoded данные тела запроса автоматически анализируются, парсятся и заносятся в $this->data для запросов PUT и DELETE. Если вы принимаете данные JSON или XML, то ниже показано, как можно получить доступ к телам этих запросов.

Для доступа к входным данным вы можете декодировать их с помощью дополнительной функции. Это полезно при взаимодействии с содержимым тела запроса XML или JSON. Дополнительные параметры для функции декодирования могут передаваться в качестве аргументов для input():

$jsonData = $this->request->input('json_decode');

Переменные среды (от $_SERVER и $_ENV)

Cake\Http\ServerRequest::env($key, $value = null)

ServerRequest::env() - это оболочка для глобальной функции env() и действует как получатель/установщик переменных среды без изменения глобальных переменных $_SERVER и $_ENV:

// Получить хост
$host = $this->request->env('HTTP_HOST');

// Задать значение, обычно полезно при тестировании.
$this->request->env('REQUEST_METHOD', 'POST');

Чтобы получить доступ ко всем переменным среды в запросе, используется getServerParams():

$env = $this->request->getServerParams();

Новое в версии 3.4.0: Метод getServerParams() добавлен в 3.4.0

XML или JSON данные

Приложения, использующие REST, часто обмениваются данными без URL-кодирования. Прочитать входные данные любого формата, можно используя Http\\ServerRequest::input(). Указывая функцию декодирования, вы можете получать контент в десериализованном формате:

// Получить JSON-кодированные данные, представленные в экшен PUT/POST запросом
$jsonData = $this->request->input('json_decode');

Некоторые методы десериализации требуют дополнительных параметров при вызове, таких как параметр «ассоциативный массив» при использовании json_decode. Если вы хотите преобразовать XML в объект DOMDocument, Http\ServerRequest::input() поддерживает передачу дополнительных параметров:

// Получить XML-кодированные данные, представленные в экшен PUT/POST запросом
$data = $this->request->input('Cake\Utility\Xml::build', ['return' => 'domdocument']);

Информация о пути (Path)

Объект запроса также предоставляет информацию о путях в вашем приложении. Атрибуты base и webroot полезны для создания URL-адресов и определения того, находится ли ваше приложение в подкаталоге. Возможно использование следующих атрибутов:

// Предположим, что текущий URL-адрес запроса: /subdir/articles/edit/1?page=1

// Присвоит переменной '/subdir/articles/edit/1?page=1'
$here = $request->getRequestTarget();

// Присвоит переменной '/subdir'
$base = $request->getAttribute('base');

// Присвоит переменной '/subdir/'
$base = $request->getAttribute('webroot');

// До версии 3.4.0:
$webroot = $request->webroot;
$base = $request->base;
$here = $request->here();

Проверка условий запроса

Cake\Http\ServerRequest::is($type, $args...)

Объект запроса обеспечивает простой способ проверки определенных условий в запросе. Используя метод is(), можно проверить ряд общих условий, а также другие критерии запроса конкретного приложения:

$isPost = $this->request->is('post');

Так же, можно расширить условия проверки запросов, используя Cake\Http\ServerRequest::addDetector() для создания новых типов детекторов. Существует четыре разных типа детекторов, которые могут быть созданы:

  • Сравнение значений среды - сравнивает значение, полученное из env() с предоставленным значением;
  • Сравнение по шаблону - позволяет сравнить значение, полученное из env(), с представленным значением, через регулярное выражение;
  • Сравнение на основе опций - использует список вариантов для создания регулярного выражения;
  • Детекторы обратного вызова - позволяют выдавать «обратный вызов» для обработки проверки. Обратный вызов получит объект запроса в качестве единственного параметра.

Cake\Http\ServerRequest::addDetector($name, $options)

Вот некоторые примеры:

// Добавить детектор сравнения значения среды.
$this->request->addDetector(
    'post',
    ['env' => 'REQUEST_METHOD', 'value' => 'POST']
);

// Добавить детектор сравнения по шаблону.
$this->request->addDetector(
    'iphone',
    ['env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i']
);

// Добавить детектор параметров
$this->request->addDetector('internalIp', [
    'env' => 'CLIENT_IP',
    'options' => ['192.168.0.101', '192.168.0.100']
]);

// Добавить детектор обратного вызова. Должно быть допустимым.
$this->request->addDetector(
    'awesome',
    function ($request) {
        return $request->getParam('awesome');
    }
);

// Добавить детектор, который использует дополнительные аргументы. По состоянию на 3.3.0
$this->request->addDetector(
    'controller',
    function ($request, $name) {
        return $request->getParam('controller') === $name;
    }
);

Request также включает в себя такие методы, как: Cake\Http\ServerRequest::domain(), Cake\Http\ServerRequest::subdomains() и Cake\Http\ServerRequest::host(), чтобы помочь облегчить жизнь при создании приложений с субдоменами.

Существует несколько встроенных детекторов, которые вы можете использовать:

  • is('get') Проверка, является ли текущий запрос запросом GET;
  • is('put') Проверка, является ли текущий запрос запросом PUT;
  • is('patch') Проверка, является ли текущий запрос запросом PATCH;
  • is('post') Проверка, является ли текущий запрос запросом POST;
  • is('delete') Проверка, является ли текущий запрос запросом DELETE;
  • is('head') Проверка, является ли текущий запрос запросом HEAD;
  • is('options') Проверка, является ли текущий запрос запросом OPTIONS;
  • is('ajax') Проверка, поступил ли текущий запрос с помощью X-Requested-With = XMLHttpRequest;
  • is('ssl') Проверка, выполняется ли запрос через SSL;
  • is('flash') Проверка, имеет ли запрос в User-Agent Flash;
  • is('requested') Проверка, имеет ли запрос параметр ‘requested’ со значением 1;
  • is('json') Проверка, имеет ли запрос расширение «json» и принимает MIME-тип «application/json»;
  • is('xml') Проверка, имеет ли запрос расширение «xml» и принимает MIME-тип ‘application/xml’ или ‘text/xml’.

Новое в версии 3.3.0: Детекторы могут принимать дополнительные параметры начиная с версии 3.3.0

Данные сессии (Session Data)

Для доступа к сессии текущего запроса используется метод session():

$userName = $this->request->session()->read('Auth.User.name');

Для получения дополнительной информации см. Документацию по использованию объекта сессии (Sessions)

Хост и доменное имя

Cake\Http\ServerRequest::domain($tldLength = 1)

Возвращает имя домена, в котором работает ваше приложение:

// Выведет 'ВашДомен.рф'
echo $request->domain();

Cake\Http\ServerRequest::subdomains($tldLength = 1)

Возвращает субдомены, в которых работает ваше приложение, в виде массива:

// Вернет ['my', 'dev'] для 'my.dev.example.org'
$subdomains = $request->subdomains();

Cake\Http\ServerRequest::host()

Возвращает хост, на котором установлено ваше приложение:

// Выведет например 'my.dev.example.org'
echo $request->host();

Чтение HTTP-метода

Cake\Http\ServerRequest::getMethod()

Возвращает метод HTTP, на основе которого был выполнен запрос:

// Выведет например POST
echo $request->getMethod();

// До версии 3.4.0
echo $request->method();

Ограничение того, какой метод HTTP может принимать экшен

Cake\Http\ServerRequest::allowMethod($methods)

После установки допустимых методов HTTP, если они не будут согласованы с запросом, выбросится исключение MethodNotAllowedException. Ответ 405 будет включать заголовок с указанием разрешенных методов:

public function delete()
{
    // Будут приниматься только запросы POST и DELETE
    $this->request->allowMethod(['post', 'delete']);
    ...
}

Чтение заголовков HTTP

Позволяет получить доступ к любому из заголовков HTTP_ *, которые были использованы для запроса. Например:

// Получить заголовок в виде строки
$userAgent = $this->request->getHeaderLine('User-Agent');

// Получить массив всех значений.
$acceptHeader = $this->request->getHeader('Accept');

// Проверяет, существует ли заголовок
$hasAcceptHeader = $this->request->hasHeader('Accept');

// До версии 3.4.0
$userAgent = $this->request->header('User-Agent');

Хотя некоторые установки Apache делают заголовок при авторизации не доступным, CakePHP сделает его доступным по специальным методам apache в случае необходимости.

Cake\Http\ServerRequest::referer($local = false)

Возвращает URL адрес источника запроса.

Cake\Http\ServerRequest::clientIp()

Возвращает IP-адрес текущего посетителя.

Заголовки доверенного прокси

Если ваше приложение находится за балансировщиком нагрузки или работает в облачном сервисе, часто бывает необходимо получать от них хост, порт и схему в своих запросах. Такие балансировки нагрузки также отправляют заголовки HTTP-X-Forwarded- * с исходными значениями. Пересылаемые заголовки не будут использоваться CakePHP из коробки. Чтобы объект запроса использовал эти заголовки, для свойства trustProxy необходимо установить значение true:

$this->request->trustProxy = true;

// Эти методы теперь будут использовать проксированные заголовки.
$port = $this->request->port();
$host = $this->request->host();
$scheme = $this->request->scheme();
$clientIp = $this->request->clientIp();

Проверка принимающих заголовков

Cake\Http\ServerRequest::accepts($type = null)

Можно узнать, какие типы данных (Content-Type) принимает клиент, или проверьте, принимает ли он конкретный Content-Type.

Получить все типы:

$accepts = $this->request->accepts();

Проверка конкретного типа:

$acceptsJson = $this->request->accepts('application/json');

Cake\Http\ServerRequest::acceptLanguage($language = null)

Получить все языки, принятые клиентом, или проверить, принят ли конкретный язык.

Получить список принятых языков::

$acceptsLanguages = $this->request->acceptLanguage();

Проверьте, принят ли конкретный язык::

$acceptsSpanish = $this->request->acceptLanguage('es-es');

Cookie (куки)

Куки запроса можно прочитать несколькими способами:

// Получить значение cookie или null, если указанная cookie отсутствует.
$rememberMe = $this->request->getCookie('remember_me');

// Прочитать значение или получите значение по умолчанию 0
$rememberMe = $this->request->getCookie('remember_me', 0);

// Получить все куки как хэш
$cookies = $this->request->getCookieParams();

// Получить экземпляр CookieCollection (начиная с версии 3.5.0)
$cookies = $this->request->getCookieCollection()

См. документацию по Cake\Http\Cookie\CookieCollection для работы с экземпляром коллекции файлов куки.

Новое в версии 3.5.0: ServerRequest::getCookieCollection() добавлен в версии 3.5.0

Ответ (Response)

class Cake\Http\Response

Cake\Http\Response - это класс ответа по умолчанию в CakePHP. Он объединяет в себе определенный функционал для генерации HTTP ответов приложения. Cake\Http\Response также помогает при тестировании, поскольку его можно обойти/пропустить, для проверки заголовков, которые будут отправлены. Подобно Cake\Http\ServerRequest, Cake\Http\Response объединяет ряд методов, ранее относящихся к контроллеру: RequestHandlerComponent и Dispatcher. Старые методы устарели в пользу использования Cake\Http\Response.

Response предоставляет интерфейс для объединения задач, связанных с ответом, таких как:

  • Отправка заголовков для перенаправления;
  • Отправка content type заголовков;
  • Отправка прочих заголовков;
  • Отправка тела ответа.

Работа с типами контента

Cake\Http\Response::withType($contentType = null)

Вы можете управлять Content-Type в ответах приложения с помощью Cake\Http\Response::withType(). Если приложению нужно иметь дело с Content-Types, которые не встроены в Response, их можно также сопоставить с type():

// Добавить тип vCard
$this->response->type(['vcf' => 'text/v-card']);

// Установить в ответе Content-Type vcard.
$this->response = $this->response->withType('vcf');

// До версии 3.4.0
$this->response->type('vcf');

Обычно необходимо отображать дополнительные Content-Type в обратном вызове beforeFilter() контроллера, для этого можно использовать функции автоматического переключения вида RequestHandlerComponent, при его использовании.

Отправка файлов

Cake\Http\Response::withFile($path, $options = [])

Бывают случаи, когда необходимо отправлять файлы в качестве ответов на запросы. Это можно делать, используя Cake\Http\Response::withFile():

public function sendFile($id)
{
    $file = $this->Attachments->getFile($id);
    $response = $this->response->withFile($file['path']);
    // Верните ответ, чтобы предотвратить попытку контроллера
    // отобразить представление.
    return $response;
}

// До 3.4.0
$file = $this->Attachments->getFile($id);
$this->response->file($file['path']);
// Верните ответ, чтобы предотвратить попытку контроллера
// отобразить представление.
return $this->response;

Как показано в приведённом выше примере, необходимо в метод передать путь к файлу. CakePHP отправит соответствующий заголовок типа контента, если это известный тип файла, указанный в Cake\Http\Reponse::$_mimeTypes. Вы можете добавлять новые типы до вызова Cake\Http\Response::withFile() с помощью метода Cake\Http\Response::withType().

Также можно принудительно загрузить файл вместо отображения страницы в браузере, указав параметры:

$response = $this->response->withFile(
    $file['path'],
    ['download' => true, 'name' => 'foo']
);

// До 3.4.0
$this->response->file(
    $file['path'],
    ['download' => true, 'name' => 'foo']
);

Поддерживаемые параметры:

  • name - позволяет указать альтернативное имя файла для отправки пользователю;
  • download - логическое значение, указывающее, следует ли устанавливать заголовки для принудительной загрузки.

Отправка строки в виде файла

Так же, можно ответить файлом, который не существует на диске, например, pdf или ics, сгенерированным «на лету» из строки:

public function sendIcs()
{
    $icsString = $this->Calendars->generateIcs();
    $response = $this->response;
    $response->body($icsString);

    $response = $response->withType('ics');

    // Необязательно принудительно загружать файл
    $response = $response->withDownload('filename_for_download.ics');

    // Возвратить объект ответа, чтобы предотвратить попытку
    // контроллера отобразить представление.
    return $response;
}

Обратные вызовы также могут возвращать тело в виде строки:

$path = '/some/file.png';
$this->response->body(function () use ($path) {
    return file_get_contents($path);
});

Настройка заголовков

Cake\Http\Response::withHeader($header, $value)

Настройка заголовков выполняется с помощью метода Cake\Http\Response::withHeader(). Как и все методы интерфейса PSR-7, этот метод возвращает экземпляр new с новым заголовком:

// Добавить/заменить заголовок
$response = $response->withHeader('X-Extra', 'My header');

// Установка нескольких заголовков
$response = $response->withHeader('X-Extra', 'My header')
    ->withHeader('Location', 'http://example.com');

// Добавить значение в существующий заголовок
$response = $response->withAddedHeader('Set-Cookie', 'remember_me=1');

// До 3.4.0 - Установить заголовок
$this->response->header('Location', 'http://example.com');

Заголовки не отправляются при установке. Вместо этого они сохраняются до тех пор, пока не будет получен ответ Cake\Http\Server.

Теперь вы можете использовать удобный метод Cake\Http\Response::withLocation(), чтобы напрямую установить или получить заголовок местоположения перенаправления.

Настройка body

Cake\Http\Response::withStringBody($string)

Чтобы установить строку в качестве тела ответа, необходимо выполнить следующие действия:

// Установите строку в тело
$response = $response->withStringBody('My Body');

// Если вам нужен json-ответ
$response = $response->withType('application/json')
    ->withStringBody(json_encode(['Foo' => 'bar']));

Перевод статьи не закончен....