rest-response-formatting.md 7.04 KB
Newer Older
Dmitry Korolev committed
1
Форматирование ответа
2 3
===================

Dmitry Korolev committed
4
При обработке RESTful API запросов приложение обычно выполняет следующие шаги, связанные с форматированием ответа:
5

Dmitry Korolev committed
6
1. Определяет различные факторы, которые могут повлиять на формат ответа, такие как media type, язык, версия и т.д.
Dmitry Korolev committed
7
   Этот процесс также известен как [согласование содержимого](http://en.wikipedia.org/wiki/Content_negotiation).
Dmitry Korolev committed
8
2. Конвертирует объекты ресурсов в массивы, как описано в секции [Ресурсы](rest-resources.md).
9 10
   Этим занимается [[yii\rest\Serializer]].
3. Конвертирует массивы в строки исходя из формата, определенного на этапе согласование содержимого. Это задача для
Dmitry Korolev committed
11 12
   [[yii\web\ResponseFormatterInterface|форматера ответов]], регистрируемого с помощью компонента приложения
   [[yii\web\Response::formatters|response]].
13 14


Dmitry Korolev committed
15
## Согласование содержимого <a name="content-negotiation"></a>
16

Dmitry Korolev committed
17
Yii поддерживает согласование содержимого с помощью фильтра [yii\filters\ContentNegotiator]]. Базовый класс
18
контроллера RESTful API - [[yii\rest\Controller]] - использует этот фильтр под именем `contentNegotiator`.
Dmitry Korolev committed
19
Фильтр обеспечивает соответствие формата ответа и определяет используемый язык. Например, если RESTful API запрос
Dmitry Korolev committed
20
содержит следующий заголовок:
21 22 23 24 25

```
Accept: application/json; q=1.0, */*; q=0.1
```

26
Он получит ответ в JSON-формате такого вида:
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

```
$ curl -i -H "Accept: application/json; q=1.0, */*; q=0.1" "http://localhost/users"

HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
      <http://localhost/users?page=2>; rel=next,
      <http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

[
    {
        "id": 1,
        ...
    },
    {
        "id": 2,
        ...
    },
    ...
]
```

58
Под капотом происходит следующее: прежде, чем *действие* RESTful API контроллера будет выполнено, фильтр
Dmitry Korolev committed
59
[[yii\filters\ContentNegotiator]] проверит HTTP-заголовок `Accept` в запросе и установит, что
60 61
[[yii\web\Response::format|формат ответа]] должен быть в `'json'`. После того, как *действие* будет выполнено и вернет
итоговый объект ресурса или коллекцию, [[yii\rest\Serializer]] конвертирует результат в массив.
Dmitry Korolev committed
62
И, наконец, [[yii\web\JsonResponseFormatter]] сериализует массив в строку в формате JSON и включит ее в тело ответа.
63

64
По умолчанию, RESTful API поддерживает и JSON, и XML форматы. Для того, чтобы добавить поддержку нового формата,
Dmitry Korolev committed
65
вы должны установить свою конфигурацию для свойства [[yii\filters\ContentNegotiator::formats|formats]] у фильтра
Dmitry Korolev committed
66
`contentNegotiator`, например, с использованием поведения такого вида:
67 68 69 70 71 72 73 74 75 76 77 78

```php
use yii\web\Response;

public function behaviors()
{
    $behaviors = parent::behaviors();
    $behaviors['contentNegotiator']['formats']['text/html'] = Response::FORMAT_HTML;
    return $behaviors;
}
```

Dmitry Korolev committed
79 80
Ключи свойства `formats` - это поддерживаемые MIME-типы, а их значения должны соответствовать именам
форматов ответа, которые установлены в [[yii\web\Response::formatters]].
81 82


Dmitry Korolev committed
83
## Сериализация данных <a name="data-serializing"></a>
84

85
Как уже описывалось выше, [[yii\rest\Serializer]] - это центральное место, отвечающее за конвертацию объектов ресурсов
Dmitry Korolev committed
86
или коллекций в массивы. Он реализует интерфейсы [[yii\base\ArrayableInterface]] и [[yii\data\DataProviderInterface]].
Dmitry Korolev committed
87 88
Для объектов ресурсов как правило реализуется интерфейс [[yii\base\ArrayableInterface]], а для коллекций -
[[yii\data\DataProviderInterface]].
Dmitry Korolev committed
89

Dmitry Korolev committed
90 91 92 93
Вы можете переконфигурировать сериализатор с помощью настройки свойства [[yii\rest\Controller::serializer]], используя
конфигурационный массив. Например, иногда вам может быть нужно помочь упростить разработку клиентской части
приложения с помощью добавления информации о пагинации непосредственно в тело ответа. Чтобы сделать это,
переконфигурируйте свойство [[yii\rest\Serializer::collectionEnvelope]] следующим образом:
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108


```php
use yii\rest\ActiveController;

class UserController extends ActiveController
{
    public $modelClass = 'app\models\User';
    public $serializer = [
        'class' => 'yii\rest\Serializer',
        'collectionEnvelope' => 'items',
    ];
}
```

Dmitry Korolev committed
109
Тогда вы можете получить следующий ответ на запрос `http://localhost/users`:
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

```
HTTP/1.1 200 OK
Date: Sun, 02 Mar 2014 05:31:43 GMT
Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
X-Powered-By: PHP/5.4.20
X-Pagination-Total-Count: 1000
X-Pagination-Page-Count: 50
X-Pagination-Current-Page: 1
X-Pagination-Per-Page: 20
Link: <http://localhost/users?page=1>; rel=self,
      <http://localhost/users?page=2>; rel=next,
      <http://localhost/users?page=50>; rel=last
Transfer-Encoding: chunked
Content-Type: application/json; charset=UTF-8

{
    "items": [
        {
            "id": 1,
            ...
        },
        {
            "id": 2,
            ...
        },
        ...
    ],
    "_links": {
        "self": "http://localhost/users?page=1",
        "next": "http://localhost/users?page=2",
        "last": "http://localhost/users?page=50"
    },
    "_meta": {
        "totalCount": 1000,
        "pageCount": 50,
        "currentPage": 1,
        "perPage": 20
    }
}
```