我们在哪里处理ASP中的错误?. NET WebAPI,维护瘦控制器和关注点分离



所以当我使用WebAPI并试图维护瘦控制器时,我试图找出在哪里处理异常。

一个简单的例子是通过电子邮件检索用户。我的API首先需要验证用户是否存在。如果不是,它需要返回404。

为了维护瘦控制器,我只想简单地将请求传递给某个地方的服务。我希望避免将存储库注入到控制器中。现在,首先检查用户是否存在是服务的责任。如果是,返回用户。当用户不存在时,我就会感到困惑。

当用户不存在时,服务是否有责任抛出HttpException?如果是这样,那么该服务就不会被视为域服务,而可能是一个特殊的rest验证服务?如果是这样,它是否抓住用户,然后在用户确实存在时将用户传递给域服务,以避免对数据存储的多次调用?

在一个更复杂的场景中,我可能想要向购物车中添加一项商品。控制器将把post请求传递给正确的服务。然后,服务将验证一些业务规则:购物车存在、最大订单未超过等。

这是两种不同类型的验证。将它们分离到单独的验证服务中感觉是正确的,否则我最终会将域验证与rest验证混合在一起。

我想我正在努力弄清楚如何维护关注点的分离,并将域验证转换为rest验证。

如果有人能给我一些启示或指出正确的方向,那就太好了!

在这种情况下,我的一般选择是在服务/存储库中使用特定于域的异常,例如UserNotFoundException。我不认为服务干预web容器的动态是一个好主意。您可能希望在控制台应用程序中重用相同的服务,等等。

那么您可能会捕获此特定于域的异常并生成404页面。我同意你的选择,不让它失控。您可以为异常指定错误处理程序。有不同级别的错误处理程序。您可以为每个控制器定义一个,也可以为整个应用程序定义一个。或者你可以使用注解来标记控制器动作,这些动作将使用你的错误处理程序。

我认为购物车的情况也支持这种设计。您使用验证服务来验证您的购物车。如果验证不成功,则购物车服务抛出ValidationException。您的错误处理程序可能捕获ValidationException并生成适当的响应代码。

错误处理程序的一个好处是,当发生某个错误时,更改需求有时会迫使您运行一些业务逻辑。如果您将错误处理与错误处理程序隔离开来,那么操作错误处理的逻辑将很容易。

这是一个非常好的问题,我赞赏您将存储库与控制器分开,这很棒。您还应该将HttpStatus代码排除在服务之外。以下是我推荐的模式。

<标题> API控制器h1> 他们想象成"交通警察",他们把交通引向不同的服务,这就是他们所做的。它们不应该包含业务逻辑。例子:
    [HttpGet]
    [Route("api/v1/codes/uidRequest/{uidRequestKey}")]
    [ResponseType(typeof(UIDRequestUIDsModel))]
    public async Task<IHttpActionResult> ViewUIDs(string uidRequestKey)
    {
        var uidRequestKeyGuid = uidRequestKey.ToNullableGuid();
        if (uidRequestKeyGuid == null)
        {
            return BadRequest($"{uidRequestKey} is not a valid guid");
        }
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        var validatedResult = await _uidRequestApiService.GetUIDRequestUIDsAsync(uidRequestKeyGuid.Value, CurrentUserIdentity);
        return ParseValidatedResult(validatedResult);
    }
在上面的例子中,请注意所有的业务逻辑都是在不同的API服务类中完成的。然后,API控制器可以解析该结果并返回正确的状态码(401、404等)。

API服务类

这是业务逻辑应该存在的地方。为了使其尽可能成为单元可测试的,它应该不应该返回HttpStatus代码或类似的东西。这将成为一场测试噩梦,并将服务层与api层紧密耦合。

从域验证转换为REST验证

这是我们做这个的类。

protected IHttpActionResult ParseValidatedResult<TApiModel>(ValidatedResult<TApiModel> apiResult) where TApiModel : class
{
    if (apiResult == null)
    {
        return NotFound();
    }
    if (apiResult.Success)
    {
        return Ok(apiResult.Result);
    }
    if (!apiResult.Success && apiResult.FailureReason == ValidationFailureReason.Unauthorized)
    {
        return StatusCode(HttpStatusCode.Forbidden);
    }
    return BadRequest(string.Join("n", apiResult.Errors));
}

希望对大家有帮助。

最新更新