0

我正在将我的逻辑从 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 基础架构过于耦合。

我快速的谷歌搜索没有成功,所以我可能遗漏了一些东西。处理这种情况的推荐方法是什么?

4

1 回答 1

2

此逻辑/验证不属于命令/查询处理程序,因为它处理某些场景(WebAPI、MVC、桌面、移动)的不同事物(用户输入 -> BadRequest、资源状态(未找到、确定、已创建 (201)))应用程序等)并且响应高度依赖于基础架构(在 REST 中,您将返回状态代码、MVC 重定向、桌面消息等)

使用异常是不可接受的,因为这些是常见场景而不是特殊场景(至少输入验证和“未找到”)。在处理程序中使用IActionResult也与基础设施紧密耦合。

在更大的项目中,我使用“结果”类,类似于IdentityResultASP.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()等等。

于 2017-03-16T13:55:34.957 回答