Получить ModelState вне контроллера и ActionFilter
Есть ли способ получить доступ к ModelStateDictionary
из DI (вне контроллера или ActionFilter)? Я думаю, что я могу сделать ActionFilter, который хранит ссылку на ModelStateDictionary
где-то, так что я могу получить к нему доступ позже в другом месте, но я хочу знать, есть ли обычный способ доступа к нему, как есть IHttpContextAccessor
за HttpContext
,
У нас есть веб-приложение, которое является клиентом для нашего API. Причина, по которой я хочу это сделать, заключается в том, что я хочу автоматически добавлять ошибки в веб-приложение. ModelState
из DelegatingHandler
используется нашим клиентом API (типизированный http-клиент). Обработчик будет наблюдать за каждым ответом от API и, для применимых (400 ответов с пользовательскими кодами ошибок в теле, такими как "Имя уже занято"), добавить сообщение к ModelState
,
Пока я пытался попросить ControllerContext
, но, кажется, всегда ноль.
var controllerContext = _serviceProvider.GetService<ControllerContext>();
controllerContext?.ModelState.AddModelError("", result.ErrorMessage);
Я также просмотрел все зарегистрированные сервисы, использующие отладчик VS, но не нашел ничего перспективного.
Дополнительное замечание (довольно большое) в отношении комментариев о SRP и разделении интересов: я не думаю, что это нарушает SRP. Клиент API - это общая клиентская реализация нашего API, которую можно использовать из любого места (в настоящее время мы используем его в Xamarin и веб-приложении ASP.NET Core MVC - о котором упоминается в этом самом вопросе). Однако клиент API ожидает HttpClient
в его конструкторе, что означает, что его поведение может быть изменено его потребителями.
Например, веб-приложение использует DI для HttpClient
клиент API нуждается. Тот HttpClient
настроен на использование двух делегирующих обработчиков, один из которых я описал в этом вопросе.
Что касается того или нет ModelState
следует манипулировать вне контроллеров: ну, это именно то, что нравится библиотекам FluentValidation
(и проверка по умолчанию ASP.NET) делает.
Что касается того или нет ModelState
следует манипулировать в DelegatingHandler
Я думаю, что это несколько более обоснованное обсуждение. Тем не менее, никто не представил никаких аргументов в отношении того, почему это плохо.
Что касается или нет, это должно быть сделано "автоматически": я думаю, что лучше иметь код в одном месте, чем не забывать делать это каждый раз при каждом действии при каждом вызове API.
Относительно того, должны ли эти сообщения быть помещены в ModelState
: хорошо, если я зайду сюда, эта побочная заметка станет слишком большой. Кроме того, никто действительно не спорил об этом, так что...
2 ответа
Вы не можете получить ControllerContext
само по себе, но то, что вы можете получить, это ActionContext
(которая является более специализированной версией контекста контроллера). Вы можете получить его с помощью IActionContextAccessor
:
var actionContextAccessor = _serviceProvider.GetService<IActionContextAccessor>();
var actionContext = actionContextAccessor.ActionContext;
actionContext?.ModelState.AddModelError("", result.ErrorMessage);
На самом деле вам не нужно каждый раз разрешать средство доступа к контексту действия, но вы можете хранить его экземпляр и просто обращаться к контексту действия, когда это необходимо. ActionContext
будет установлен, когда вы находитесь в рамках действия при обработке запроса.
Альтернативным решением было бы разделить проблемы и сохранить ошибки где-то еще, а затем заполнить состояние модели оттуда во время фильтра действий. Таким образом, ваше решение не будет ограничено MVC, и вы также можете получить доступ к этим ошибкам в другом месте.
По моему мнению, этим вы нарушите принцип единой ответственности, поскольку http-клиент не должен ничего знать о ваших контроллерах.
Однако, если вы действительно хотите это сделать, вы можете использовать лямбда-выражения для передачи ссылки на ModelStateDictionary вашему делегату, см. Раздел: Передача функции делегата с дополнительными параметрами.
Если вы не хотите прерывать SRP, одним из вариантов будет заставить http-клиента возвращать список ошибок, которые будут добавлены контроллером в состояние модели (или ноль, если ошибок нет)