Урок 3. Requests и responses, роутинг и контроллеры
Опубликовано пн, 23/01/2017 - 23:55
Содержание
Requests и responses
Запросы и ответы в Drupal 8 представлены компонентом Symfony HttpFoundation (был рассмотрен в Уроке 2) и классами Request и Response соответственно.
Request
Начнем с запроса[1]. Простой способ создания запроса из глобальных переменных выглядит так
1 2 |
use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); |
Объект запроса содержит информацию о клиентском запросе. Данные могут быть получены через паблик свойства класса:
- request — эквивалент $_POST;
- query — эквивалент $_GET ($request->query->get('name'));
- cookies — эквивалент $_COOKIE;
- attributes — эквивалента нет, используется для хранения дополнительных данных;
- files — эквивалент $_FILES;
- server — эквивалент $_SERVER;
- headers — в основном эквивалент подгруппе $_SERVER ($request->headers->get('User-Agent')).
Каждое свойство — это экземпляр класса ParameterBag (класс, который представляет собой контейнер для хранения пары ключ/значение). Соответственно все экземпляры данного класса имеют методы для получения/обновления данных, а также фильтрации входных значений.
Чтобы получить информацию о запрашиваемом пути в классе Request существует метод getPathInfo().
Для примера обратимся к странице example.com/example
1 2 3 |
use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); $path = $request->getPathInfo(); |
В переменной $path получим значение /example.
Пример имитации запроса.
1 2 3 4 5 6 |
use Symfony\Component\HttpFoundation\Request; $request = Request::create( 'https://www.google.com/', 'GET', array('q' => 'drupal%208') ); |
Примеры неполного списка команд для получения тех или иных значений от запроса
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$request = Request::createFromGlobals(); // Возвращает путь, по которому был сделан запрос. Для example.com/example вернет /example. $request->getPathInfo(); // Получение GET и POST параметра в запросе. $request->query->get('foo'); $request->request->get('bar', 'значение по умолчанию если первого параметра не обнаружено'); // Получение SERVER переменных. $request->server->get('HTTP_HOST'); // Получение экземпляра UploadedFile идентифицируемого по foo. $request->files->get('foo'); // Получение значения COOKIE. $request->cookies->get('PHPSESSID'); // Получение заголовка HTTP запроса, с нормализованными ключами в нижнем регистре. $request->headers->get('host'); $request->headers->get('content_type'); $request->getMethod(); // GET, POST, PUT, DELETE, HEAD $request->getLanguages(); // массив клиентских языков. |
Значение которые пришли по урлу /example?foo=123
Response
Response[2] объект хранит всю необходимую информацию для построения ответа клиенту. Конструктор класса Response принимает три аргумента:
- контент ответа;
- статус код;
- HTTP заголовки;
Пример создания респонса.
1 2 3 4 5 6 7 |
use Symfony\Component\HttpFoundation\Response; $response = new Response( 'This is response', Response::HTTP_OK, array('content-type' => 'text/html') ); |
Ответ также можно модифицировать после его создания
1 |
$response->setStatusCode(Response::HTTP_NOT_FOUND); |
При отправке запроса используют методы prepare() и send(). Метод prepare() приводит response в соответствие со спецификацией RFC 2616 протокола HTTP[3].
1 2 |
$response->prepare($request); $response->send(); |
Класс Response содержит довольно большое количество методов для управления HTTP заголовками (setPublic(), setPrivate(), expire() и т.д.).
Для редиректа пользователя на другой урл компонент HttpFoundation имеет в своем арсенале класс RedirectResponse, наследуемый от базового класса Response. Пример редиректа
1 2 3 4 5 6 |
use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; $request = Request::createFromGlobals(); $response = new RedirectResponse('/'); $response->prepare($request); $response->send(); |
Более подробную информацию о работе с Response объектом можно получить по ссылке[2].
Роутинг и контроллеры
Друпаловская роутинговая система работает на основе компонента Симфони HttpKernel. Роут — это путь, определенный в Друпале, при обращении по которому возвращается определенный контент.
Как правило, большинство фреймворков или систем умеют управлять повторяющимися задачами (например роутинг, проверки доступа и т.д.), поэтому разработчики могут довольно просто создавать страницы приложения. HttpKernel компонент предоставляет интерфейс, который формализует процесс запроса и создание соответствующего ответа. Т.е. таким образом компонент является “сердцем” любого приложения или системы и не имеет значения насколько они отличаются по внутренней архитектуре.
Структура интерфейса HttpKernel приведена ниже.
1 2 3 4 5 6 7 8 9 10 11 12 |
namespace Symfony\Component\HttpKernel; use Symfony\Component\HttpFoundation\Request; interface HttpKernelInterface { // ... /** * @return Response A Response instance */ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true); } |
Метод HttpKernel:handle() — это конкретная реализация обработки запроса и возврата ответа.
Drupal на базе интерфейса HttpKernalInterface реализует собственный интерфейс HttpDrupalKernelInterface. Класс, который непосредственно содержит всю логику метода handle() называется DrupalKernel и реализуется от двух интерфейсов: HttpDrupalKernelInterface и TerminableInterface[4].
Рассмотрим, что происходит когда пользователь обращается по урлу, например /admin/config.
При запросе Drupal создает экземпляр автозагрузчика \Composer\Autoload\ClassLoader. Затем создает экземпляр класса DrupalKernel с передачей ему двух параметров: строки ‘prod’ и объекта автозагрузчика. Строка “prod” - обозначает энвайронмент (вариации: prod и dev). Энвайронмент далее участвует в построении кеш ключа для сервис контейнера. Далее получаем объект Request из глобальных переменных и передаем его в метод handle() экземпляра класса DrupalKernel. После обработки происходит отправка ответа и вызов метода terminate(), который отвечает за прерывание цикла запрос/ответ.
Как это выглядит схематически приведено ниже (упрощенная версия). Ознакомиться с полным вариантом можно по ссылке[5].
Нумерация на схеме отображает последовательность выполнения тех или иных блоков. Некоторые блоки могут не участвовать в создании ответа. Например, если контроллер возвращает объект Response, то блоки 5 и 6 (MainContentViewSubscriber и Main content renders) участвовать не будут.
Контроллер — это класс, который содержит метод, обрабатывающий запрос и возвращающий ответ. В Drupal 7 роль контроллеров выполняли page callbacks. Для указания соответствия пути (урла) и связанного с ним контроллера существует файл *.routing.yml.
Итак, что же нужно для объявления роутинга кастомного урла:
- создать файл MYMODULE.routing.yml
- Добавить в него урл, указать метод контроллера, который будет возвращать содержимое страницы, пермишены и т.д. Пример:
1 2 3 4 5 6
MYMODULE.example: path: '/example' defaults: _controller: '\Drupal\MYMODULE\Controller\MymoduleController::getOutput' requirements: _permission: 'access content'
- Создать контроллер, описать указанный метод (getOutput) в файле контроллера MymoduleController.php
1 2 3 4 5 6 7 8 9
class MymoduleController { /** * Outputs the example page. */ public function getOutput() { return t('This is example page.'); } }
Создание кастомного модуля
В данном пункте рассмотрим создание кастомного модуля. Для этого перво-наперво создадим папку нашего модуля в директории modules/custom. Данный модуль по мере написания статей и изучения новых материалов мы будет постоянно расширять и дополнять. Модуль не будет тривиальным (наподобие Hello world), а будет выполнять достаточно конкретную задачу: тянуть ленту новостей с какого-либо новостного портала. Имя модулю дадим соответствующее — news.
Итак в папке modules/custom создаем папку news.
В ней создаем файл news.info.yml. Содержимое данного файла приведено ниже
1 2 3 4 5 6 |
name: News description: 'Provides news feeds.' type: module core: 8.x package: Other version: '8.x-1.0' |
Все, модуль готов:) Этого вполне достаточно, чтобы модуль был отображен в панели управления и мы могли его включить.
Домашнее задание
Создать модуль news, добавить на страницу admin/config под блоком Web Services ссылку News с описанием. Урл ссылки News — admin/config/services/news. По этому урлу отображать пустую страницу с заголовком News.
Для выполнения данного задания потребуется создать свой роутинг, также необходимо разрешить доступ к данной странице только группе "администраторы".
Дополнительная информация по статье
- http://symfony.com/doc/current/components/http_foundation.html#request - о Request из документации симфони.
- http://symfony.com/doc/current/components/http_foundation.html#response - о Response из документации симфони.
- https://tools.ietf.org/html/rfc2616 - спецификация RFC 2616 протокола HTTP.
- http://api.symfony.com/3.2/search.html?search=TerminableInterface - TerminableInterface из документации симфони.
- https://www.drupal.org/files/d8_render_pipeline.pdf - полный вариант схемы роутинга.
- Версии программных продуктов, используемых в статье: Drupal 8.2.5
4 Комментария
AK - пн, 06/03/2017 - 12:35
Добавьте в пример создания
Добавьте в пример создания респонса:
Спасибо за уроки. Очень доходчиво.
nightdevel - вт, 07/03/2017 - 01:51
Добавил, спасибо за замечание
Добавил, спасибо за замечание.
is - вс, 18/06/2017 - 08:18
В контроллере похоже ошибка,
В контроллере похоже ошибка, метод должен называться getOutput вместо getExample.
nightdevel - вс, 18/06/2017 - 21:53
Благодарю за внимательность,
Благодарю за внимательность, исправил.