Доступ к ControlerBase вне действия (в обработчике CQRS)

Я перемещаю свою логику из Controller в обработчики CQRS, и я не уверен, как обращаться с кодом, который вызывает ControllerBase, например:

[HttpPut("users/{id}")]
public IActionResult Put(UserForm userForm)
{
  var user = Users.Get(email);
  if (user == null) 
    return NotFound();
  ...
}

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

  1. Вместо этого создайте пользовательское NotFoundException, затем перехватите его в ExceptionMiddleware и верните туда NotFoundResult.
  2. Внедрить 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(), так далее.

Другие вопросы по тегам