Нельзя просто так взять и сделать версионирование API
Transcript of Нельзя просто так взять и сделать версионирование API
![Page 1: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/1.jpg)
НЕЛЬЗЯ ПРОСТО ТАК ВЗЯТЬ И СДЕЛАТЬВЕРСИОНИРОВАНИЕ API
github.com/ikalnytskyi
![Page 2: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/2.jpg)
МОТИВАЦИЯЗабота о пользователях
![Page 3: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/3.jpg)
Tweetbot
Gwibber
TweetDeck
Twitter API
IFTTT.com
RememberTheMilk.com
![Page 4: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/4.jpg)
МОТИВАЦИЯЗабота о пользователяхОбнаружение поддерживаемого функционала
![Page 5: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/5.jpg)
{ "owner": "John Doe", "product": { ... } }
{ "owner": { "firstName": "John", "lastName": "Doe" }, "product": { ... } }
{ "owner": { "firstName": "John", "lastName": "Doe", "phone": "+1 234 567 89" }, "product": { ... } }
![Page 6: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/6.jpg)
import requests
response = requests.get('https://api.not-ebay.com/sales') entity = response.json()
if isinstance(entity['owner'], dict): if 'phone' in entity['owner']: # Последняя версия API, предоставляем весь # полный набор функционала. else: # Предпоследняя версия API, предоставляем # ограниченный набор функционала. else: # Боль. :'( Самая старая версия API, предоставляем # только базовый набор функционала.
![Page 7: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/7.jpg)
МОТИВАЦИЯЗабота о пользователяхОбнаружение поддерживаемого функционалаОбновление с минимальным временем простоя
![Page 8: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/8.jpg)
Сервер #1
nova-api
nova-conductor
nova-scheduler
python-novaclient
nova-compute
Сервер #2
nova-api
nova-conductor
nova-scheduler
python-novaclient
nova-compute
Сервер #3
nova-api
nova-conductor
nova-scheduler
python-novaclient
nova-compute
Коммуникатор(Load Balancer, AMQP, etc)
![Page 9: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/9.jpg)
![Page 10: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/10.jpg)
СПОСОБЫ ВЕРСИОНИРОВАНИЯ HTTP APIВерсионирование при помощи URI
![Page 11: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/11.jpg)
GET /v1.42/resource HTTP/1.1 Host: api.example.com
![Page 12: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/12.jpg)
СПОСОБЫ ВЕРСИОНИРОВАНИЯ HTTP APIВерсионирование при помощи URIВерсионирование при помощи параметра запроса
![Page 13: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/13.jpg)
GET /resource?version=1.42 HTTP/1.1 Host: api.example.com
![Page 14: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/14.jpg)
СПОСОБЫ ВЕРСИОНИРОВАНИЯ HTTP APIВерсионирование при помощи URIВерсионирование при помощи параметра запросаВерсионирование при помощи HTTP заголовка
![Page 15: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/15.jpg)
GET /resource HTTP/1.1 Host: api.example.com Api-Version: 1.42 Vary: Api-Version
![Page 16: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/16.jpg)
СПОСОБЫ ВЕРСИОНИРОВАНИЯ HTTP APIВерсионирование при помощи URIВерсионирование при помощи параметра запросаВерсионирование при помощи HTTP заголовкаВерсионирование при помощи Content Negotiation
![Page 17: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/17.jpg)
GET /resource HTTP/1.1 Host: api.example.com Accept: application/vnd.project+json; version=1.42
![Page 18: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/18.jpg)
![Page 19: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/19.jpg)
ВЕРСИОНИРОВАНИЕ ПРИ ПОМОЩИ CONTENTNEGOTIATION
Заголовок Accept и RFC 7231
![Page 20: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/20.jpg)
GET /resource HTTP/1.1 Host: api.example.com Accept: application/xml; q=0.2, application/json, application/* Accept: application/vnd.project+json; version=1; q=0.7 Accept: application/vnd.project+json; q=0.9; version=2
![Page 21: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/21.jpg)
ВЕРСИОНИРОВАНИЕ ПРИ ПОМОЩИ CONTENT NEGOTIATION
Content-Type дилемма
![Page 22: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/22.jpg)
POST /resource HTTP/1.1 Host: api.example.com Accept: application/vnd.project+json; version=1 Content-Type: ???
{ "owner": "John Doe", "product": { ... } }
![Page 23: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/23.jpg)
POST /resource HTTP/1.1 Host: api.example.com Accept: application/vnd.project+json; version=1 Content-Type: application/vnd.project+json; version=1
{ "owner": "John Doe", "product": { ... } }
![Page 24: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/24.jpg)
POST /resource HTTP/1.1 Host: api.example.com Accept: application/vnd.project+json; version=1 Content-Type: application/vnd.project+json; version=2
{ "owner": { "firstName": "John", "lastName": "Doe" }, "product": { ... } }
![Page 25: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/25.jpg)
POST /resource HTTP/1.1 Host: api.example.com Accept: application/vnd.project+json; version=1 Content-Type: application/json
{ "owner": "John Doe", "product": { ... } }
![Page 26: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/26.jpg)
Согласно :RFC 7231
Заголовок Accept определяет предпочтения по типупредставления ответа от сервера, включаязапрашиваемый ресурс и возможные ошибки.
Заголовок Content-Type определяет типпредставления передаваемого в теле сообщенияданных.
![Page 27: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/27.jpg)
ОХ.. МОЖЕТ ПОПРОБОВАТЬ HTTP ЗАГОЛОВОК?
![Page 28: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/28.jpg)
ВЕРСИОНИРОВАНИЕ ПРИ ПОМОЩИ HTTPЗАГОЛОВКА
Какой выбрать код ответа если запрашиваемаяверсия API не найдена?
400 Bad Request404 Not Found406 Not Acceptable412 Precondition Failed
Убедиться, что выбранный заголовок не фильтруетсяиспользуемым стеком технологий.
![Page 29: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/29.jpg)
ХМ.. А ЧТО С ПАРАМЕТРОМ ЗАПРОСА?
![Page 30: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/30.jpg)
ВЕРСИОНИРОВАНИЕ ПРИ ПОМОЩИПАРАМЕТРА ЗАПРОСА
Традиционно используются совместно с методомGET.
Некоторые веб-фреймворки могут быть не готовы ктакому порядку вещей.
![Page 31: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/31.jpg)
django 1.10
@require_http_methods(['POST']) def create_user(request): version = request.GET.get('version', LATEST_VERSION)
![Page 32: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/32.jpg)
ОК, ПОПРОБУЕМ URI
![Page 33: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/33.jpg)
ВЕРСИОНИРОВАНИЕ ПРИ ПОМОЩИ URI
Не требует никаких специальных возможностейфреймворка. В простом случае, каждая версия -отдельный обработчик.
Самый популярный способ версионирования HTTPAPI.
![Page 34: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/34.jpg)
ВЕРСИОНИРОВАНИЕ И ВЕБ-ФРЕЙМВОРКИ
Фреймворки не решают проблему версионированияAPI.
В лучшем случае существуют расширения кфреймворкам, которые позволяют автоматическиизвлечь версию и передать ее в обработчик.
![Page 35: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/35.jpg)
djangorestframework 3.5.3
from rest_framework import views, versioning
class CreateUser(views.APIView):
versioning_class = versioning.QueryParameterVersioning
def post(self, request, format=None): if request.version == '42': pass
![Page 36: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/36.jpg)
DIY: ВЕРСИОНИРОВАНИЕ И ВЕБ-ФРЕЙМВОРКИ
Подход к версионированию тесно связан с подходомк организации кода.
Один обработчик принимающий версию API?Много обработчиков вызываемых в зависимости отверсии?
В сочетании со способами передачи версииклиентом, имеем немалое количество вариантов.
![Page 37: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/37.jpg)
Решение задачи обычно сводится к написаниюсобственного middleware или роутера.
![Page 38: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/38.jpg)
![Page 39: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/39.jpg)
ЭВОЛЮЦИЯ ДАННЫХ
Эволюция API - это следствие эволюции данных.
Расширение функциональности зачастую ведет кэволюции данных.
![Page 40: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/40.jpg)
database = [ {'owner': 'John Doe', 'product': some_product_data}, ]
@app.route('/v1/sales') def get_sales_v1(): return jsonify(database)
![Page 41: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/41.jpg)
database = [ {'owner': {'firstName': 'John', 'lastName': 'Doe'}, 'product': some_product_data}, ]
@app.route('/v1/sales') def get_sales_v1(): conv_owner = lambda o: '{firstName} {lastName}'.format(**o) return jsonify([ {'owner': conv_owner(record['owner']), 'product': record['product']} for record in database ])
@app.route('/v2/sales') def get_sales_v2(): return jsonify(database)
![Page 42: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/42.jpg)
ЭВОЛЮЦИЯ ТЕСТОВВместе с эволюцией данных, следует эволюция тестов.
![Page 43: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/43.jpg)
Каждая версия API должна быть покрыта тестамидабы гарантировать что все работает так как иработало, а формат запроса/выдачи не поменялся.
Изменение схемы данных требует адаптациифейковых данных в тестах.
По возможности не использовать mock притестировании версий API.
![Page 44: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/44.jpg)
# Актуальный формат: # # {'owner': # {'firstName': 'John', # 'lastName': 'Doe'}, # 'product': some_product_data}, # _database = [ {'owner': 'John Doe', 'product': some_product_data}, ]
@mock.patch('project.database', _database) def test_get_sales_v1(app): with app.test_client() as c: assert c.get('/v1/sales').json() == { 'owner': 'John Doe', 'product': some_product_data, }
![Page 45: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/45.jpg)
ЭВОЛЮЦИЯ БИЗНЕС ЛОГИКИ
Разные версии API могут требовать разной версиибизнес логики.
Необходимо быть готовым к наличию несколькихальтернативных реализаций одной и той же функции.
Следуя принципу DRY, очень легко оказаться в адунаследований, стратегий и сложных интерфейсов.Альтернатива – copy-paste подход.
![Page 46: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/46.jpg)
def assign_ips_lt_5_0(...): pass
def assign_ips_eq_5_0(...): pass
def assign_ips_gt_6_1(...): pass
![Page 47: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/47.jpg)
ВЫВОДЫИх нет. Каждый их должен сделать сам. :)
![Page 48: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/48.jpg)
МОИ ВЫВОДЫ
Наличие нескольких версий API существенноувеличивает цену поддержки.
Не поддерживать все множество существующихверсий. Выбрать стратегию. Например, поддерживатьпоследние N версий.
Подумать об отказе от версионирования в случаезакрытого продукта, если нет требований кобновлению с минимальным временем простоя.
![Page 49: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/49.jpg)
Использовать HTTP заголовок для версионированиякак самый простой и гибкий способ.
Хранить тесты на каждую поддерживаемую версиюAPI. Не использовать mock.
![Page 50: Нельзя просто так взять и сделать версионирование API](https://reader034.fdocument.pub/reader034/viewer/2022050914/586f8f331a28ab54768b748d/html5/thumbnails/50.jpg)
СПАСИБО ЗА ВНИМАНИЕ!ВОПРОСЫ?