Комментарии 17
Прям в тему статья, только недавно озадачился этой темой: https://ru.stackoverflow.com/q/1085978/213987
IModelBinderProvider хорошо использовать для каких-то вещей которые есть вот прям в каждм реквесте. По дефолту, мне лично больше нравится расставлять аттрибуты, потом легче читать.
Вопрос вкусов. На мой вкус, атрибут нужен если иногда модель берется из одного источника, а иногда из другого. Если же всегда источник один, то постоянное прописывание атрибута только замыливает глаз, как и любой шаблонный код.
Кроме того, мы неизбежно столкнемся со случаем, когда разработчик забудет добавить атрибут.
«Злачные места» аргумент немного странный.
Вы, наверное, правы, стоило объяснить по-другому. Я лишь недавно, с переходом на 3.1, начал закрывать POCO. И результат мне дико понравился. Основная причина — единая инициализация через конструктор, просто не позволяющая создавать некорректный объект.
Классический случай: добавилось новое необязательное поле в запросе. В тестах при этом запросы создаются по-старому, без этого поля. Результат? Тесты не упали, но они уже невалидны.
Кроме того, можно логику, связанную с созданием объекта (например, простановку серверного времени создания) унести в конструктор, а не писать по всему проекту.
Ну и пропавшие бесконечные if (obj.IsValid) радуют неимоверно. Конструктор гарантирует валидность созданной модели, внешние проверки не нужны.
А если с ней, то получается, что вводится новое жесткое правило: во всех тестах, где создается какой-либо POCO, должна быть проверка на его валидность. И никакой гарантии соблюдения правила. Нет, можно конечно настроить автоматику… Но это кажется диким оверхедом.
Насчет ActionFilter — в этом случае он как раз не поможет, т.к. срабатывает до и после вызова метода конструктора, а в стандартном кейсе модель конструируется внутри.
С таким подходом часть данных доставляется магическим/неявним образом. В итоге просто задебажить контролер не получиться, всегда нужно это учитывать. Кроме того, как писать тесты с такой магией? Запускать весь asp net core runtime чтобы магия начала работать?
Тут было бы логичней вынести все это в отдельные сервисы. Первый отвечает за роботу с другим API, делая все необходимые валидации. Второй уже успользует его и делает свой набор валидаций. Он же пакует все вместе и возвращает. В случае ошибки делать исключение или Maybe<> с кодом ошибки.
Почему же, код прекрасно дебажится. Просто по пайплайну обработки запроса приведенный метод сработает ДО того, как вызовется сам контроллер.
Насчёт юнит тестов: сам класс, понятное дело, тестируется нормально. Если же нужен интеграционный тест, проверяющий что binder запускается (что странно, вы же не проверяете запуск дефолтного binder), то нужна таже самая магия, что и для теста Model Validation с рантаймом. Но на то они и интеграционные тесты.
Ваш вариант, можно сказать, "классический". Я его привел в самом начале статьи. Только вы предлагаете унести и проверки внутрь сервисов. Это частично решит проблему повторяемости, но усугубит самую главную проблему — "размазанность" валидации. Получив некорректный запрос я должен дать отлуп сразу, ещё до его попадания в контроллер, а не где-то в недрах бизнес-логики. На этом принципе построены Authorization и Model Validation в .Net Core и кажется логичным следовать принципам, заложенным в платформу, на которой пишешь.
Да, еще стоило заметить что мы используем .Net Core 3.1 в «параноидальном» режиме (warning as errors), который очень нервно относится к публичным полям ссылочного типа. Да, можно заткнуть его "= null!", но вот это как раз костыль и путь в Бездну.
.Net Core Api: получение данных в запросе из разных источников