[PHP, Разработка под iOS, API, Dart, Flutter] Уродливый API

Автор Сообщение
news_bot ®

Стаж: 6 лет 2 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
12-Апр-2021 14:31

ВступлениеВ этой статье хочу рассказать о проблемах, с которыми столкнулся в процессе интеграции с API по HTTP протоколу, и поделиться опытом их решения.При разработке фронтенд приложений (Mobile/Web), часто сталкиваешься с тем, что API на бэкенде еще не реализован. Приходится ждать разработчика на бэкенде, когда он предоставит нужные запросы, постоянно напоминая ему о себе. Не редкость и другая ситуация, когда нужные http запросы уже есть, но они реализованы очень плохо и криво.Возможно, я бы и не писал эту статью. Но мне показался поразительным тот факт, что все приведенные ниже примеры плохой реализации API попались мне в одном-единственном проекте, одновременно!В этом проекте я разрабатываю мобильное приложение на Flutter, используя пакет Retrofit, который помогает мне сократить время и код, который приходится писать самому, генерируя значительный код автоматически. Так же использую Insomnia, для первоначальной проверки запросов до реализации их в коде.Итак, начнем.Не-RESTful архитектураПервым огорчением стал тот факт, что API архитектура реализована не в стиле RESTful. Честно сказать, я и не помню, когда приходилось сталкиваться с интеграцией таких не-RESTful APIs.REST означает REpresentational State Transfer. RESTful — вид реализации архитектуры API, которая наилучшим образом позволяет использовать протокол HTTP. С REST нам нужно думать о приложении с точки зрения ресурсов. Определить, какие ресурсы мы хотим открыть для внешнего мира (например, tasks, customers, etc.). Используем глаголы, определенные протоколом HTTP, для выполнения CRUD операций с этими ресурсами, т.к. GET, POST, PUT, DELETE. Пример RESTful API:
Архитектура API моего проекта — не REST архитектура — выглядит следующим образом:
Можно заметить, что все запросы имеют один тип, это POST запросы. И каждый запрос должен содержать параметр type, который определяет операцию. И, видимо, на сервере, в этом одном-единственном endpoint’е имеется какой-то if-else или switch операторы на проверку этого type параметра.Я, конечно, предпочитаю RESTful, но раз уж другая реализация нормально работает, и на клиенте можно более-менее настроить запросы с Retrofit, я принял этот факт и продолжил интегрироваться с APIHeader Accept: application/jsonОбычно сначала я оформляю запросы на сервер в Insomnia, проверяю их, и после реализую их в коде. В Insomnia, когда я выполнил запрос, во вкладке Preview увидел красивый json и подумал, что ко мне приходит json объект. Я оформил все это в коде, Retrofit мне конвертирует ответ запроса в Dart объект автоматически, и все замечательно сработало.Но на следующий день моё приложение перестало работать из-за ошибки, связанной с запросом к серверу. Текст ошибки был: “не получается преобразовать строку в объект”. Я снова вернулся к Insomnia и проверил запрос. В Preview я увидел тот же json, что и раньше. И только после проверки Header запроса я обнаружил, что Content-Type изменился, и значение его уже text/html, charset-utf-8, хотя Preview показывает мне json. Таким образом, я определил, что тип ответа от сервера изменился с application/json на text/html, из-за чего Retrofit уже не может преобразовать ответ от сервера типа строки автоматически в Dart объект.Я решил попробовать использовать Accept Header в запросе, который скажет серверу, что “я - клиент ожидаю от тебя ответ в формате json”. Но это не сработало, т.к. сервер не берет в расчет этот Header Accept.
Тут мне снова пришлось внести ещё больше кода, чтобы обойти эту проблему с типом ответа сервера:
  • Добавил новую версию запроса в Retrofit, где во второй версии я ожидаю тип “строка”
  • Далее начал вызывать новую версию запроса, которая возвращает строку, и добавил преобразование строки в объект. Между тем, это преобразование Retrofit мог бы произвести самостоятельно в своем сгенерированном коде. А сейчас в каждом месте, где мы выполняем запросы на сервер, получая ответы, мы должны добавлять это преобразование сами:

В итоге, добавилось ещё больше строк кода, что никак не радует.JSON keys case typesБывает несколько видов оформления названий полей в json:
В идеале нужно выбрать один понравившийся вариант, и придерживаться его во всём проекте и во всех запросах. Но в моём проекте бэкенд разработчик решил использовать несколько видов:
В Dart в идеале для именования полей класса и методов используется camelCase стиль. И если от сервера приходит json с полями в camelCase стиле, то Retrofit автоматически, без усилий с нашей стороны, правильно сопоставит поля класса и json. Но в данной ситуации мне приходится указывать дополнительные аннотации JsonKey, которые в одном случае как snake_case, а в другом —  UPPER_CASE_SNAKE_CASE:
В итоге — нет единообразия, если ошибёшься в case type, то можешь потерять значение, т.к. поля json b Dart класса не сопоставятся. Я разозлился еще больше на API, но интеграцию с API всё же можно было продолжать, затрачивая больше усилий и ещё больше кода.Различный ответ на один и тот же запросКогда я попробовал выполнить вход в приложение под другим пользователем, моё приложение снова сломалось. Я получил сообщение об ошибке типа “Невозможно преобразовать строку в число”. Это было связано с тем, что мой класс, описывающий ответ от сервера, стал неверным. Типы, описанные в классе, не соответствуют типам полей, имеющимся в json ответе. Я снова открыл Insomnia, и выполнил один и тот же запрос входа для двух разных пользователей. И обнаружил, что в одном случае числа приходят в ответе в виде строк, а в другом — в виде чисел.  В Insomnia можно заметить, что строки выделены желтым цветом, а числа — фиолетовым:
Как вообще может быть, что на один и тот же запрос приходит один и тот же json, но с разными типами значений? Неужели на бэкенде стоит условие на пользователя и нужно возвращать разный json? Зачем? Как?
Я знаю, что сервер написан на PHP. Господа, кто программирует на PHP, могли бы вы написать в комментариях, как такое возможно?ЗаключениеСейчас я испытываю противоположные чувства. С одной стороны, я расстроен, что приходится интегрировать такой уродливый API, с другой — уже жду с нетерпением следующий кейс, который покажет, как же ещё можно изуродовать API.Хотелось бы узнать о вашем опыте интеграции с API, и с какими проблемами вы сталкивались. Спасибо. Happy coding!
===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_php, #_razrabotka_pod_ios (Разработка под iOS), #_api, #_dart, #_flutter, #_api, #_json, #_flutter, #_dart, #_php, #_httpserver (http-сервер), #_ios_razrabotka (ios разработка), #_android_development, #_php, #_razrabotka_pod_ios (
Разработка под iOS
)
, #_api, #_dart, #_flutter
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 06-Май 21:50
Часовой пояс: UTC + 5