Про RestFul в Drupal 8

Главные вкладки

Аватар пользователя LLC VelvetDev LLC VelvetDev 18 июля 2017 в 13:03
4

Как-то мой друг сказал, что Drupal 8 - это ползающий ребенок, который не понимает, что происходит вокруг. За этим смешно наблюдать, но всегда нужно быть на чеку.

В общем-то, он прав. Но я бы хотел поделиться чем-то, что мне нравится и что уже успешно применено на практике.

Речь пойдет о RESTful Web Services API.

Эта новинка Drupal 8 дает возможность сторонним разработчикам использовать (и изменять) данные приложений. Например, взаимодействие с мобильными приложениями или фронтэнд построен таким образом, что от сервера требуются только данные, а отрисовка HTML происходит на клиенте.

Эти функции как-то в один момент сглаживают неуклюжесть младенца и сразу появляется желание понаблюдать за ним еще, не правда ли? Давайте разбираться дальше.

Методы HTTP

  • GET для получения данных (SELECT).

В случае “удачного” (или не содержащего ошибок) адреса, GET возвращается представление ресурса в формате XML или JSON (в зависимости от значения переменной _format) в сочетании с кодом состояния HTTP 200 (OK).
В случае наличия ошибок обычно возвращается код 404 (NOT FOUND) или 400 (BAD REQUEST).

По этому поводу есть отличный пример:

        GET http://example.com/api/v1/program/1?_format=json
        HEADERS: Content-Type: application/json
  • POST для добавления данных (INSERT)

При успешном создании ресурса возвращается HTTP код 201, а также в заголовке ‘Location’ передается адрес созданного ресурса.

Пример:

        GET http://example.com/entity/user/?_format=json
        HEADERS:
                Content-Type: application/json
                Authorization: Basic XXXX
                X-CSRF-Token: XXXX  - получить его можно по этому пути http://example.com/rest/session/token
        BODY:
                {
                        "name":[{"value":"test"}],
                        "status":[{"value":"1"}],
                        "mail":[{"value":"test@test.fr"}],
                        "pass":"test"
                }
  • PATCH (PUT) для обновления данных (UPDATE).

Если вы использовали PATCH для обновления (и оно прошло успешно), то возвращается код 200 (или 204 если не был передан какой-либо контент в теле ответа). Если PATCH используется для создания экземпляра, то при успешном выполнении обычно возвращается HTTP код 201.

  • DELETE для удаления данных.

При успешном удалении возвращается 200 (OK) код HTTP, совместно с телом ответа, содержащим данные удаленного ресурса (отрицательно сказывается на экономии трафика) или завернутые ответы (Смотрите "Возвращаемые данные"). Также возможно использование HTTP кода 204 (NO CONTENT) без тела ответа.

Как происходит вывод articles при помощи views export
Если кто-то давно думал, как же это лучше сделать – я подготовил пошаговую инструкцию.

  • Включаем следующие модули "RESTful Web Services" и "Serialization"
  • RESTful Web Services

  • Создаем REST EXPORT views
  • После завершения установки модуля нажмите «Structure» в меню администрирования:
    REST EXPORT

  • Нажать на "Views":
  • Views

  • Нажать "Add new view":
  • Add new view

Заполните форму, как показано на скриншоте ниже. Я назвал “Article REST Export”. Выберите "Article" из раскрывающегося списка в types. Поставьте галочку в поле "Provide a REST export" в разделе "REST export settings"./
Далее укажите ссылку в поле "REST export path" по которой будете получать данные. В примере это "rest/export/json/article"
REST export path

Жмем кнопку "Save and edit" и вы увидите следующую страницу.
Save and edit

Нажмите на "Settings" для выбора формата выходных данных
Settings

После сохранения Views вы можете проверить конечную точку с помощью любого клиента REST.

Еще один хороший пример запроса:

        GET http://example.com/rest/export/json/article?_format=json
        HEADERS: Content-Type: application/json
        RESPONSE:

RESPONSE

Custom REST resource

Для создания своего ресурса с нужными данными нужно:
Создать в модуле по пути /modules/YOUR_MODULE/src/Plugin/rest/resource файл ProgramListResource.php
И прописать следующее:

        <?php

namespace Drupal\my_module\Plugin\rest\resource;

use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Psr\Log\LoggerInterface;
use Drupal\my_module\MyApiFetcher;

/**
* Provides a Exercise List Resource
*
* RestResource(
*   id = "exercise_list_resource", // Машиное имя
*   label = Translation("Exercise List Resource"), // Заголовок
*   uri_paths = {
*     "canonical" = "/api/v1/exercise/list" // Ссылка по которой будет доступны данные
*   }
* )
*/

class ExerciseListResource extends ResourceBase {

 /**
  * The My API fetcher.
  *
  * var \Drupal\my_module\MyApiFetcher
  */

 protected $myApiFetcher;

 /**
  * Constructs a new ExerciseListResource instance.
  *
  * param array $configuration
  *   A configuration array containing information about the plugin instance.
  * param string $plugin_id
  *   The plugin_id for the plugin instance.
  * param mixed $plugin_definition
  *   The plugin implementation definition.
  * param array $serializer_formats
  *   The available serialization formats.
  * param \Psr\Log\LoggerInterface $logger
  *   A logger instance.
  * param \Drupal\my_module\myApiFetcher $my_api_fetcher
  *   The My API fetcher.
  */

 public function __construct(array $configuration, $plugin_id,
                             $plugin_definition, array $serializer_formats,
                             LoggerInterface $logger,
                             MyApiFetcher $slim_api_fetcher) {
   parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
   $this->myApiFetcher = $my_api_fetcher; // Динамически подгружаем данные для сбора данных
 }

 /**
  * {inheritdoc}
  */

 public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
   return new static(
     $configuration,
     $plugin_id,
     $plugin_definition,
     $container->getParameter('serializer.formats'),
     $container->get('logger.factory')->get('rest'),
     $container->get('my_api.fetcher')
   );
 }

 /**
  * Responds to entity GET requests.
  * return \Drupal\rest\ResourceResponse
  */

 public function get() { //Реализация получения (GET) данных
   $data = [];
   $exercises = $this
     ->myApiFetcher
     ->getExercises();
   if (!empty($exercises)) {
     foreach ($exercises as $exercise) {
       $data[] = [ // Запись данных в массив
         'exercise_id' => $exercise->id(),
         'exercise_title' => $exercise->getName(),
         'exercise_count' => $this->myApiFetcher->getFieldValue($exercise, 'exercise_count'),
       ];
     }
   }
   return new ResourceResponse($data);
 }

}

Создать config file где опишем resource.
Назовем файл rest.resource.program_list_resource.yml

Пропишем в его следующее:

id: program_list_resource // Машинное имя ресурса который писали в файле ProgramListResource.php
plugin_id: program_list_resource
granularity: method
configuration:
 GET:
   supported_formats: // Поддерживающие форматы данных
     - json
   supported_auth: // Тип авторизации.
     - basic_auth

Пример запроса:

        GET http://example.com/api/v1/program/list?_format=json
        HEADERS: Content-Type: application/json
        RESPONSE:
                [
                        {
                                 "program_id": "1",
                                "program_title": "Program 1",
                        },
                        {
                                 "program_id": "2",
                                "program_title": "Program 2",
                        }
                ]

На github есть пример модуля https://github.com/Ruslan03492/rest_api_example

Несмотря на то, что Drupal 8 действительно пока натыкается на все острые углы в доме, у него уже есть какие-то фишки, которые мы можем применять. Что, конечно, не отменяет состояния повышенной внимательности к тому, что происходит вокруг.

Комментарии

Аватар пользователя gun_dose gun_dose 18 июля 2017 в 17:24

Интересно, почему обычный REST, а не jsonapi, например?

fairrandir wrote:

Пример с POST почему-то с GET. Smile

По сабжу - как вообще реализовывают авторизацию в случае REST?

Login

POST: https://example.com/user/login?_format=json
Content-type: application/json

{
"name": "admin",
"pass": "password"
}

https://www.drupal.org/docs/8/core/modules/rest/javascript-and-drupal-8-...