Важливість API серіалізації виводу

Я розповідав про API *лионы раз за останній рік. Безліч відгуків і питань, що виникли в той момент, коли я говорив про серіалізації, як про "додавання шару представлення ваших даних".

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

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, конфіденційна інформація може витекти з вашого бізнесу, коли вас просять додати контактний email, призначений тільки для внутрішнього використання. Якщо ви витратили місяці на збір цих місць і збудували ряд унікальних контактів, вам не захочеться, щоб усі дані просочилися до потенційним конкурентам.

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

Наприклад, 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 мають безліч типів даних: integer, float, boolean, і інші, але всі, що користувач отримує на виході — звичайна рядок.

Замість true і false ви побачите "1" і "0", або може бути навіть "t" і "f". Числа з плаваючою точкою на виході представляють "-41.235" замість -41.235.

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

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

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

Перейменування полів
Перейменування полів в сховище даних не порушить ваш API. Якщо вас дратує необхідність оновлювати всі ваші інтеграційні тести, то подумайте, як буде дратувати це розробників мобільних додатків, або інших frontend-команд, яким необхідно оновити і розгорнути нові додатки. Можливо ви навіть не пам'ятаєте про lock-step розгортанні. Якщо ні, тоді ви збираєтеся отримати неробочі програми для кінцевих користувачів, і навіть якщо ви викотите оновлення для IOS, програми все одно залишаться неробочими до тих пір, поки користувачі не оновлять їх вручну.

Щоб уникнути цієї проблеми, вам необхідно перейменувати поле в шарі серіалізації, що дозволить оновити посилання на дані підставляється поля, без внесення змін у зовнішнє подання.

Кілька сховищ даних
У більшості з ORM-рішень процесу серіалізації, є одне оману — всі ваші дані зберігаються в одному місці. Що ж станеться, коли деякі дані перетечуть з SQL в Redis або кудись ще?

Навіть якщо ви не переміщаєте частина даних з SQL в Redis, то можете розділити одну таблицю на дві, або використовувати зведені таблиці. В такому разі, більшість ORM-сериализаторов приземляться на обличчя, якщо ви спробуєте виконати це.

Замість цього, можна використовувати патерн Репозиторій, який став популярним в Laravel, ви можете передати всі дані з будь-яких сховищ, і вони знайдуться в бібліотеці серіалізації, а сам сериализатор збереже незмінний висновок.

Версіонування сериализаторов
Раніше я версионировал сериализаторы для мажорних версій. V1 і V2 для FoodSerializer'а могли існувати обидві, маючи різні тести, і відмінно задовольняючи численні потреби API клієнта.

Формати сериализатора
Дещо Fractal досяг не повною мірою — це мультиформатні «адаптерів», але прагне виправити це у версії 1.0. Досить непогано це було реалізовано в Rails співтоваристві, і ви можете відправляти різні заголовки для отримання зовсім іншого формату даних.



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

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

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

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

0 коментарів

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