【问题标题】:What is good approach to validate http request in .NET Core 3.1 API's?在 .NET Core 3.1 API 中验证 http 请求的好方法是什么?
【发布时间】:2020-04-13 19:26:02
【问题描述】:

我是构建 API 的新手。我的项目包含三个典型层:控制器、负责业务逻辑的服务和访问数据的存储库。在执行特定操作之前,到达我的控制器的每个请求都必须经过一些验证过程。例如,请检查以下代码:

[HttpPost]
public async Task<ActionResult<TicketDTO>> CreateTicketAsync([FromBody] CreateTicketDTO ticket)
{
    try
    {
        if (ticket.Events == null)
        {
            return BadRequest(new {Message = _localizer["LackOfEventsErrorMessage"].Value});
        }

        var user = await _userService.GetUserByIdAsync(ticket.UserId);

        if (user == null)
        {
            return NotFound(new { Message = _localizer["UserNotFoundErrorMessage", ticket.UserId].Value });
        }

        var invalidTicket = await _ticketService.CheckHasUserPlayedAnyOfGamesBeforeAsync(ticket);

        if (invalidTicket)
        {
            return BadRequest(new { Message = _localizer["EventsRepeatedByUserErrorMessage"].Value });
        }

        var createdTicket = await _ticketService.AddTicketAsync(ticket);

        if (createdTicket == null)
        {
            return BadRequest(new { Message = _localizer["TicketNotCreatedErrorMessage"].Value });
        }

        return CreatedAtAction(nameof(GetTicketById), new {ticketId = createdTicket.TicketId}, createdTicket);
    }
    catch (Exception ex)
    {
        return StatusCode(StatusCodes.Status500InternalServerError,
            new
            {
                Message = ex.InnerException != null
                    ? $"{ex.Message} {ex.InnerException.Message}"
                    : ex.Message
            });
    }
}

这是我的控制器方法之一。在将票证保存到数据库之前,它必须通过一些检查。票的所有者必须存在,否则我返回未找到用户等。问题是我不太喜欢这种验证请求的方式。该方法很混乱,而且可读性不强。我想知道什么是验证请求的好方法,并在出现问题时做出正确反应(例如,如果数据库中没有用户,则返回“UserNotFoundErrorMessage”等。单个 catch 块并不能解决我的问题。我也不希望那里有多个 catch 块,我认为这也很乱。我错了吗?) 我想知道附加的 sn-p 是否违反了一些干净的代码规则?代码应该是什么样子?我做错了什么?

【问题讨论】:

  • 你可以做一些重构,但你的方法非常好,一点也不乱。不过,你问的很自以为是。你可以附加的东西是?? 运算符 -> ticket.Events ?? throw new Exception... 或者随便什么
  • 我会将它下推到服务层并实现一个Validation&lt;T&gt; 来跟踪其有效性。如果有效,它将有一个T,如果不是,它将有一个IEnumerable&lt;Error&gt;
  • 如果你有很多验证要做,你可以看看 Fluent Validation。可能会帮助您从控制器中获取一些逻辑。

标签: c# asp.net-core controller coding-style


【解决方案1】:

所有这些逻辑都应该洗牌到您的业务层,即您的服务中。那么,服务方法应该返回一个“结果”类,这基本上只是一种将多位信息作为返回返回的方式,即成功/失败状态、错误(如果有)、实际结果查询什么的等等。然后,您可以简单地打开错误并做出相应的响应。

就捕获而言,尤其是仅返回 500 的主要捕获,使用全局异常处理程序。让错误从操作中冒出来,并依靠全局处理程序返回适当的响应。

【讨论】:

  • 你好克里斯,我认为这个问题是题外话。他/她已经使用了服务层。我认为可以在控制器中进行验证。此外,全局异常处理程序更多的是评论而不是答案。
  • 服务是合适的地方。服务中的代码适用于该服务的每次使用,而控制器中的代码将仅适用于该一个操作,并且必须一遍又一遍地重复使用该服务的任何其他操作。像这样的验证是业务层的领域,而不是表示层。
  • 你是对的。这应该在服务层作为创建工单操作。由于OP要求的其他原因。 OP 方法非常干净,只有轻微的重构才能使它变得更干净。我认为这个问题是题外话。
【解决方案2】:

正如其他人已经指出的那样,这似乎还不错。

我可以告诉你,我们的 sn-ps 代码是这个大小的 10 倍。 Tbh,与我在公司代码库中看到的一些模块相比,这似乎只是一小部分。

话虽如此,您可以将更多逻辑从控制器移到其他层。例如,当通过其 ID 获取用户时,如果该 ID 的用户不存在,您还可以从您的服务类中抛出未找到的异常。你现在已经把所有东西都塞进了一个控制器,而感觉这更像是服务的责任。

您可以做的另一件事是使用中间件:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1

您可以为您的响应创建验证管道。

我也尝试过使用验证模式。在这种情况下,我会创建一些检查规则,并将这些规则应用于要验证的内容。然后,我有一个验证器对象,它将所有规则提示一个适当的输出。这使代码更清晰,更好地重用,但是增加了一些复杂性,我最终没有使用它。它与代码库的其他部分不同,因此对同事来说是陌生的,因此我有充分的理由不使用它。

【讨论】:

  • 也许不是一个好的答案,但我希望它能给你一些想法。
  • "当通过其 ID 获取用户时,如果该 ID 的用户不存在,您还可以从您的服务类中抛出未找到的异常" - 只有当用户不存在时才应该这样做真正的非凡。通常情况并非如此。
  • 好吧——那在设计上对我来说似乎有点陌生。无论如何,这不是重点,重点是您可以将该逻辑从控制器移到服务中:)
猜你喜欢
  • 1970-01-01
  • 2021-05-31
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
  • 2019-08-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多