Доступ к ControlerBase вне действия (в обработчике CQRS)
Я перемещаю свою логику из Controller в обработчики CQRS, и я не уверен, как обращаться с кодом, который вызывает ControllerBase, например:
[HttpPut("users/{id}")]
public IActionResult Put(UserForm userForm)
{
var user = Users.Get(email);
if (user == null)
return NotFound();
...
}
Поэтому, когда я перемещаю эту логику за пределы действия, очевидно, что у меня нет доступа к NotFound
больше. Я вижу несколько способов решить эту проблему:
- Вместо этого создайте пользовательское NotFoundException, затем перехватите его в ExceptionMiddleware и верните туда NotFoundResult.
- Внедрить ControllerBase в команды / запросы CQRS. Не нравится этот параметр, так как команды / запросы будут слишком связаны с инфраструктурой ASP.NET Core.
Быстрый поиск в Google не увенчался успехом, поэтому, возможно, я что-то упустил Каков рекомендуемый подход к такой ситуации?
1 ответ
Эта логика / проверка не относится к обработчикам команд / запросов, поскольку она обрабатывает разные вещи (пользовательский ввод -> BadRequest, состояние ресурса (NotFound, Ok, Created (201))) для определенных сценариев (WebAPI, MVC, Desktop, Mobile Приложение и т. Д.) И ответы сильно зависят от инфраструктуры (в REST вы должны вернуть код состояния, перенаправление MVC, сообщения рабочего стола и т. Д.)
Использование исключений недопустимо, так как это распространенные сценарии, а не исключительные (по крайней мере, проверка входных данных и "не найдены"). И используя IActionResult
внутри обработчиков также тесно связана инфраструктура.
В больших проектах я использую классы "result", похожие на IdentityResult
в ASP.NET Core Identity.
namespace MyProject.Common
{
public class QueryResult
{
private static readonly QueryResult success = new QueryResult { Succeeded = true };
private readonly List<QueryError> errors = new List<QueryError>();
public static QueryResult Success { get { return success; } }
public bool Succeeded { get; protected set; }
public IEnumerable<QueryError> Errors { get { return errors; } }
public static QueryResult Failed(params QueryError[] errors)
{
var result = new QueryResult { Succeeded = false };
if (errors != null)
{
result.errors.AddRange(errors);
}
return result;
}
}
public class QueryResult<T> : QueryResult where T : class
{
public T Result { get; protected set; }
public static QueryResult<T> Suceeded(T result)
{
var queryResult = new QueryResult<T>
{
Succeeded = true,
Result = result
};
return queryResult;
}
}
}
И вернуть их из команд (QueryResult
) или запросы (QueryResult<T>
).
Затем вы можете написать методы расширения, которые возвращают правильные IActionResult
,
public static class QueryResultExtensions
{
public IActionResult ToActionResult(this QueryResult result)
{
if(result.Success)
{
return new OkResult();
}
if(result.Errors != null)
{
// ToModelStateDictionary needs to be implemented by you ;)
var errors = result.Errors.ToModelStateDictionary();
return BadRequestResult(errors);
}
return BadRequestResult();
}
public IActionResult ToActionResult<T>(this QueryResult<T> result)
{
if(result.Success)
{
if(result.Result == null)
{
return new NotFoundResult();
}
return new OkObjectResult(result.Result);
}
if(result.Errors != null)
{
// ToModelStateDictionary needs to be implemented by you ;)
var errors = result.Errors.ToModelStateDictionary();
return BadRequestResult(errors);
}
return BadRequestResult();
}
}
Тогда просто позвоните в свой контроллер
[HttpPut("users/{id}")]
public IActionResult Put(CreateUserCommand command)
{
var result = commandHandler.Handle(command);
return result.ToActionResult();
}
Вы теряете способность иметь более детальную способность контролировать коды возврата. Вы можете создавать разные типы результатов для команд / запросов или разные методы расширения, которые обрабатывают его по-разному .ToCreateActionResult()
, .ToQueryActionResult()
, так далее.