Управление форматом ответов#пост
Недавно в чате Yii Флудилочная
@Kutuzov_ska столкнулся с проблемой разных ответов в dev и prod окружениях приложения на фреймворке Symfony. О проблеме можно почитать
здесь.
DevelopmentКак правило, когда мы разрабатываем приложение, мы хотим видеть как можно подробнее причину поломки нашего кода:
- Текст сообщения
- Файл, строку и код, где произошла поломка
- Stacktrace от точки входа в приложения до этой самой строчки
- Значения переменных, аргументов функций и прочее.
Обычно приложения с обработчиком исключений и ошибок это и делают: рисуют красивый UI, рисуют сниппет с ошибой и подсвечивают нужную строчку.
Даже в JSON API многие приложения конвертируют всё в JSON представление и клиенты этого API могут увидеть полный текст ошибки, stacktrace и т.п.
ProductionОднако, в продакшене такое делать непозволительно опасно:
- Тексты ошибки дают злоумышленникам возможно эксплуатировать ваше приложение и найти уязвимости
- Stacktrace может раскрыть вашу внутреннюю структуру приложения
- Значения переменных могут раскрыть ваши данные для доступа к тем или иным сервисам
Чтобы такого не происходило, хорошей практикой считается сокрытие всех подробностей ошибки или исключения в конечном ответе, а залогировать ошибку можно “как есть”, считая безопасным попадания в систему сбора логов.
О надежности последнего можно говорить много, однако я бы хотел сделать упор на текст ошибки, выводимый в тело ответа.
Для клиентов API порой удобно завязаться на текст ошибки и сделать какое-то конкретное действие:
- Подождать какое-то время и отправить запрос заново
- Показать текст ошибки пользователю
- Выполнить какую-то логику, основываясь на тексте ошибки
Конечно, можно завязаться на HTTP код ответа и выполнять нужное действия, основываясь на значении этого кода, но текст ошибки иногда тоже приходится задействовать.
Чтобы ваш клиент не показал пользователю ошибку
Fatal error: Uncaught TypeError: find(): Argument #1 ($id) must be of type int, string given
Стоит как-то разделять текст, который можно показать пользователю и текст, который стоит скрыть за стандартным текстом HTTP кода:
Internal server error,
Bad request и другие.
Обработчик ошибокТакое разделение, как правило, делают на стороне формирования этой ошибки – в нашем случае на бекенде.
В Yii2 есть концепция HttpException: есть несколько базовых классов, отражающие соответствующий HTTP код исключения и возможность задать свой текст ошибки, который увидит клиент. Подробнее можно почитать здесь:
ссылка на документацияю.
В Symfony приложениях тоже есть аналогичный подход.
Exception MapperВ своих проектах я предпочитаю написать свой mapper исключений в выводимый ответ:
- Создаю класс, который встраивается в логику обработки исключений
- Сформирует нужный мне формат ответа с дополнительными кодами и текстами
- Будет заниматься прочими удобствами для development окружения
Похожая схема есть и в Yii3:
ссылка на документацию.
В Yii3 http stack построен на базе PSR middleware, поэтому нужно лишь добавить middleware в стек и настроить класс формирования ошибок.
В Symfony http stack построен на событийной модели, и для похожего действия нужно написать свой обработчик события
onKernelException.
В Symfony приложениях я создаю несколько интерфейсов-маркеров, который могут задавать кастомный текст ошибки, который сможет увидеть клиент. Все остальные ошибки скрывают все свои детали.
Выглядит такой обработчик примерно так:
ссылка на gist.
Имея собственный обработчик, этого позволяет мне внедрять множество коротких путей в разработке, уменьшая boilerplate кодовой базы. Выглядит очень просто и всегда можно изменить массово под требования.
——
Полезные ссылки:List of http status codesYii Флудилочная@handle_topic