Важливість контролю виведення сериализующего API



У даній статті автор розглядає питання зміни представлення даних та об'єктів, і косяків, які за цим зміною неминуче слідують. Він пропонує в таких випадках скрізь і всюди використовувати сериализаторы, які здатні без втрат конвертувати один формат представлення до іншого. Це дозволяє не тільки передавати дані по мережі і зберігати у файли. Коли у вас є налаштована бібліотека серіалізації, ви можете змінити сховище даних, коли вам зручно і без шкоди для проекту. Також стає легко повертати відповіді в тому вигляді, в якому їх зручно отримати запитуючій стороні.

Дисклеймер. Тільки що почав читати habrahabr.ru/post/260975. Переклад дуже кострубатий, халтурний. Я почав було писати автору більш звичні для нашого вуха варіанти перекладу деяких фраз, щоб допомогти йому це все поправити, але потім зрозумів, що при прочитанні спотикаюся на кожній третій фразі. Вирішив запив цілком свій варіант. Хоча це і не зовсім по-чесному, але змушувати читати такого рівня переклади теж не дуже по-людськи. Я б навіть сказав, дуже по-машинної. Ну так нехай спільнота розсудить. Текст дійсно непростий, свої коментарі я буду залишати курсивом. Перекладаю неглядя вже переклад drondo. Якщо де співпаде — міняти не буду. Все-таки я перекладаю англійські слова на найбільш схожі російські слова, як і drondo, але ж Диявол криється в деталях. Про опечатки будь ласка повідомте в приват, я вранці все виправлю. Спасибі.

За рік я тисячу разів говорив про небезпечні місця в API. Тільки в 2015 це було на:


Частина доповіді, яку я називаю «додаємо шар подання даних», де я кажу про серилиализации, отримала великий відгук.

MSDN говорить про це так:
Серіалізація це процес перетворення об'єкта в потік байтів з метою зберегти об'єкт чи передати його в пам'ять, базу даних або файл. Її основне призначення полягає у збереженні стану об'єкта, щоб мати можливість відтворити його при необхідності. Зворотний процес називається десериализация.
Джерело: MSDN Programming Concepts


PHP-розробники часто під сериализацей розуміють застосування функції serialize(). Так, це один з варіантів, але не єдиний. Інший поширений спосіб серіалізації звичайно ж функція json_encode(). Сучасні фреймворки автоматично перетворять в JSON будь масив, який повернувся з методу контролера. А значить вам навіть не доведеться вызвывать json_encode().

Це дуже зручно, але якщо ви створюєте HTTP API (AJAX/RESTful/Hypermedia), то потрібно бути вкрай уважним з тим, що ви виводите.

Ось вам найбільш часті граблі:
<?php
class PlaceController extends CoreController
{
public function show($id)
{
return Place::find($id);
}
}


Вибачаюся за вкрай спрощений приклад, суть у тому, що ми говоримо про моделі (можливо використовує ORM) і про повернення результату.

На вигляд все дуже мило, але в підсумку буде купа проблем.

Вистачить секретів
Кожен метод, який ви додасте в ваше сховище даних, в результаті вирушить у відповідь викликав API. Якщо це внутрішнє API, то ще не так страшно. Але якщо ця інформація проскочет десь в браузер, або в якійсь інший зовнішній джерело типу мобільного пристрою, то все буде дуже хріново.

Найочевидніший приклад паролі користувачів. Вони звичайно ж зашифровані, але ви ж явно не хочете віддавати їх іншим людям. Витік не так кидаються під увагу речей типу токенів скидання пароля може привезти до злому ваших користувачів.

Але все може статися ще непомітніше. У прикладі з «place» (прим.пер.: PlaceController) з бізнес-процесу може виникнути необхідність додати «контактний email» для внутрішнього користування. Якщо ви витратили місяці праці та налагодили унікальні зв'язку (прим. пер.: у бізнесі), вам не захочеться, щоб ці поштові адреси витекли до ваших конкурентів.

Так, у багатьох ORM можна вказати список видимих або прихованих властивостей. Але з плином часу шанси приховати всі важливі параметри тануть. Особливо якщо ви обзавелися junior'ом, який ще не в курсі, що одне з цих полів суперсекретно. А втомлений перевіряючий важким робочим днем дозволить всьому цій справі просочитися непоміченим.

Ось один із прикладів з Fractal — моєї PHP-бібліотеки для серіалізації, яку я зробив, щоб спростити серіалізацію в моїх API:

<?php
use League\Fractal\Manager;
use League\Fractal\Resource\Collection;

// Create a top level instance somewhere
$fractal = new Manager();

// Ask the ORM for books 10
$books = Book::take(10)->get();

// Turn this collection 
$resource = new Collection($books, function(array $book) {
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => (int) $book->yr,
'author' => [
'name' => $book->author_name,
'email' => $book->author_email,
],
'links' => [
[
'rel' => 'self',
'uri' => '/books/'.$book->id,
]
]
];
});

Так, тут все знову вкрай спрощене, і використовуються колбэки замість класів, але основна ідея зрозуміла.

Такі ж інструменти знайдуться для кожної мови на світі. Я працював з ActiveModel Serializer. Він майже такий же.

Незалежно від мови, який ви використовуєте на цьому тижні, я б хотів пояснити вам, чому це так важливо. Як це робити, ви можете дізнатися самі. А ця стаття — про чому.

Типи даних атрибутів
Коли справа стосується драйверів прив'язки даних, багато мов, включаючи PHP, виявляються не дуже розумні. У таких штук начебто MySQL або PostgreSQL багато всяких типів даних: цілі числа, флоаты, булев тип і т. д. Але те, що приходить юзеру — це завжди рядок.

Замість true і false ви бачите «1» і «0», або навіть «t» і «f». Флоаты з -41.235 перетворюються в "-41.235".

Для мови зі слабкою типізацією це може здатися не дуже-то і важливим. Але мови зі строгою типізацією повилітають, якщо раптом трапиться така зміна. Пару раз я бачив, як рядок з числом перетворюється в integer через якийсь математики в акцессоре ORM, в якому«1» + «2» = 3. Таке перетворення в принципі може пройти ваші юніт-тести, якщо вони недостатньо чіткі. Але вашому iOS додаток це просто знесе голову.

ActiveRecord в Rails відстежить який у поля повинен бути тип даних, коли вони добавляютя в схему через міграції. Але якщо щось зміниться — в акцессорах, або при зміні типу схеми — то чекай біди.

Якщо використовувати серіалізацію як у прикладі вище, то ви зможете самі скастовать тип ваші дані, і при виведенні бути впевненими, що це саме той тип, і він зміниться тільки якщо ви самі його поміняєте в сериализаторе.

Зміна імені поля
Перейменування полів в сховище даних не повинно ламати ваше API. Якщо вам здається надто складним оновлювати всі ваші тести, то уявіть як розробникам мобільних додатків і іншим фронтэнд командам, яким потрібно оновлювати і деплоить нові додатки. Може ви навіть забули lock-step (прим.пер.: потрібна допомога з перекладом цієї справи. найближчим російське слово, як це ні смішно, виходить «запаралелити») деплой. Якщо так, то ваші користувачі опиняться з неробочими додатками, які, навіть якщо ви випустите апдейт на iOS, так і будуть неробочими, поки вони їх не оновлять.

Все що вам потрібно, щоб обійти це пекло зміни імені поля, це шар серіалізації, який дозволить вам оновити посилання на полі, не змінюючи його зовнішнє подання.

Різні сховища даних
Багато з цих ORM'вих способів серіалізації мають одне дуже важливе припущення: всі ваші дані весь час живуть в одному і тому ж сховище. А що буде якщо частина ваших даних покине SQL і переїде в Redis або ще куди?

Навіть якщо ви і не переносили частина даних з SQL в Redis, може ви розділили свою таблицю на дві? Або стали використовувати зведені таблиці? Більшість ORM сериализаторов просто всеруться якщо ви спробуєте зробити так. (прим.пер.: якщо кому цікаво, то там було «приземляться на своє обличчя» — will land on their face. Т. к. в цей вираз вкладається чимала частка жорстокості і брутальності, вирішив перевести на це от дуже підходяще слово.)

А може замість цього скористаєтеся паттерном «репозиторій», який став так популярний у Laravel? Ви можете передати всі дані звідки завгодно, де вони зберігаються, в сериализатор, а сериализатор подбає про консистентности результату.

Версії сериализаторов
Колись у мене були сериализаторы різних версій. v1 і v2 FooSerializer можуть існувати обидві, кожна зі своїми тестами, і задовольнять різні потреби клієнтів API.(прим.пер. я не зрозумів, до чого це він. Можливо v1 і v2 настільки різні, що сприймаються як самостійні продукти і існують паралельно)

Формати сериализаторов
Те, що ще не вдалося Fractal, але планується виправити до v1.0 — це різні адаптери» форматів. Таке вже робили в співтоваристві Rails — можна було відправляти різні заголовки і отримувати різні формати відповіді.



Відправивши mime-тип ви можете сказати сериализатору в якому форматі надсилати відповідь, не засмічуючи весь ваш код потенційно складною логікою.

Рішення
Я міг би наводити причини дні на проліт, але сьогодні мій літак сів в п'ять ранку, а хлопці на сусідніх кріслах не давали мені спати всю ніч.

Я розкрив тему «чому» в серіалізації, але не «як». Про це рекомендую поглянути на ці рішення:


Я чув прекрасний долклад на RailsConf 2015 мого нового друга João Moura, який називався Історія кохання AMS, API, Rails і разраба, в якому розкривається якась крута функціональність.

Яку б систему ви ні вибрали — там скрізь приблизно одна ідея.

Якщо ви робите API будь-якого виду — будь ласка, використовуйте це.

API це не просто проксі для SQL команд. Його потрібно планувати, обдумувати і дбайливо підтримувати. І тоді просте зміни місця зберігання даних не угробить всі ваші мережеві додатки і сервіси.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.