Для кого исключения?

В настоящее время я занимаюсь созданием REST API со Spring и несколькими другими библиотеками. Частично это, конечно, правильная обработка всех исключений, которые могут возникнуть, и правильное представление их пользователю API.

И, пытаясь исчерпать все возможные исключения, я быстро понял: Эти исключения не предназначались для меня!

Два типа исключений

Для целей этого поста я разделю исключения на два разных вида, или, скорее, на два разных варианта использования, и расскажу о них отдельно:

  1. Внутренние исключения, которые важны только для вас и
  2. внешние исключения, предназначенные для пользователя вашего API.

Под внутренними исключениями я подразумеваю исключения, которые никогда не покинут код, за который вы отвечаете. Самый простой способ распознать их — если у вас не возникнет проблем с внесением в них критических изменений.

С другой стороны, внешние исключения — это исключения, которые вы (обязательно) не будете перехватывать. Пользователи не обязательно должны быть конечными пользователями вашего продукта, но могут быть и другими командами в той же компании.

Создавайте исключения для себя

Исключения первого типа менее сложны в разработке. Но я всегда задавался вопросом, почему вы создаете их в первую очередь.

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

Я не говорю, что они никогда не должны существовать, но если вы решите создать внутреннее исключение, убедитесь, что вы можете обосновать, почему.

Один из случаев, когда вы не можете избежать внутренних исключений, — это когда вы используете библиотеку, которая их генерирует. В этом случае вы являетесь потребителем внешнего исключения, но вам все равно нужно обращаться с ним как с внутренним.

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

Исключения дизайна для других

Это самая сложная часть — и та, в которой некоторые библиотеки, которые мы используем для нашего REST API, работают неправильно.

Передавая исключения своим пользователям, вы говорите им, что что-то пошло не так. Вы также должны сообщить им, что пошло не так, как можно подробнее. По сути, это просто еще одно возможное возвращаемое значение вашего API.

Предоставьте пользователю метаинформацию

На самом верхнем уровне пользователь вашего API должен знать:

  • Это моя вина, что это исключение произошло? Я перепутал ввод или какую-то настройку?
  • Если я сделаю то же самое еще раз, будет ли это иметь тот же эффект или это сработает на этот раз?
  • Должен ли я уведомлять кого-либо, когда это происходит, или ожидается, что это произойдет иногда?

Вы могли заметить, что для интерфейсов REST вы можете выяснить все эти вопросы, взглянув на код состояния возврата HTTP-вызова. В API-интерфейсах кода это обычно обозначается классом исключения.

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

Информация должна быть на том же уровне допуска

Одна библиотека синтаксического анализа, которую мы использовали, вызвала исключение с довольно красивым сообщением об ошибке, которое вы могли представить пользователю напрямую. Подумайте что-то вроде «Свойство ‘id’ не может быть пустым». Но после того, как мы немного поиграли с библиотекой, оказалось, что есть особый случай, когда она не имеет такого красивого сообщения об ошибке, а вместо этого просто оборачивает другое исключение и наследует его сообщение. Подумайте что-нибудь вроде «Невозможно преобразовать java.util.ArrayList в java.util.Set».

Обычно вы никогда не должны возвращать сообщение об исключении напрямую из библиотеки. Это просто означало бы, что ваше сообщение также может внезапно измениться при обновлении библиотеки и что вы не можете знать все возможные фразы. Но иногда это заманчиво (особенно для менее опытных программистов — к которым я себя отношу).

Особенно, когда ваша библиотека, вероятно, будет использоваться близко к пользовательскому интерфейсу (например, библиотека JavaScript), вам нужно либо убедиться, что вся возможная информация либо всегда удобочитаема для среднего конечного пользователя, либо ясно дать понять, что это не так.

В приведенном выше случае они могут либо попытаться получить лучшее сообщение даже в этом особом случае, либо выдать другое исключение. Или, что еще лучше, не слишком полагайтесь на сообщение, а предоставьте информацию отдельно (см. следующий пункт).

Честно говоря, это вина не столько библиотеки, сколько пользователя. Тем не менее, хороший API не позволит своим пользователям совершать ошибки (или, по крайней мере, усложнит их).

Информация должна быть деконструируемой

Это, пожалуй, самый важный момент: не думайте, что вашему пользователю нужна информация именно в том формате, который вы предоставляете.

Это не только делает более явным, какая информация включена, но пользователь также может выбирать только части информации и решать, как ее представить. Это позволяет вам позже добавить дополнительную информацию, которая может быть более конфиденциальной.

Другим очевидным вариантом использования этого являются переводы на другие языки. Исключение синтаксического анализа должно предоставить мне имя свойства и ошибку, выбранную из четко определенного конечного набора возможных ошибок. Тогда я сам выберу, как это сформулировать, и даже смогу перевести на разные языки.

Другой распространенный вариант использования — использование нескольких библиотек для вещей, которые, по мнению пользователя, являются одним и тем же. Например, синтаксический анализ двух разных JSON, которые придерживаются известных схем и, следовательно, имеют свои собственные выделенные парсеры.

Одна библиотека может вернуть «Идентификатор свойства не может быть пустым», а другая — «Неверный идентификатор: строка не должна быть пустой». Оба предложения говорят об одном и том же, но если пользователю представляются оба из них одновременно, это обязательно поднимет бровь.

Резюме

Как и во всем, что мы создаем, важно помнить о пользователе. Неважно, конечный пользователь нашего продукта или коллеги-программисты. Обеспечьте их соответствующими инструментами для выполнения своей работы.

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

Но так же, как обычно возвращается значение «успех», исключения также должны быть разработаны с осторожностью.