我是构建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块不能解决我的问题。)也不希望在那里有多个捕获块,我认为这也很混乱。我错了吗?)我想知道所附的代码片段是否违反了一些干净的代码规则?代码看起来如何?我做错了什么?
所有这些逻辑都应该混入您的业务层,即您的服务中。然后,服务方法应该返回一个“结果”类,这基本上只是一种将信息的多个位作为返回发送回去的方法,即成功/失败状态,错误(如果有),实际结果(如果有)。查询等。然后,您只需打开错误并做出相应响应即可。
就捕获而言,尤其是仅返回500的主要捕获,请使用全局异常处理程序。让错误从操作中冒出来,并依靠全局处理程序返回适当的响应。
[就像其他人已经指出的那样,这似乎并不坏。
我可以告诉您很多,我们的代码片段是此代码片段的10倍。待定,与我公司代码库中的某些模块相比,这似乎只是很小的一部分。
话虽如此,您可以将更多逻辑从控制器移到其他层。例如,当通过其ID获取用户时,如果不存在具有该ID的用户,则还可以从服务类中抛出未找到的异常。您现在已经将所有内容填充到控制器中,而感觉这更像是服务的可重复性。
您可能要做的另一件事是使用中间件:
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-3.1
您可以为您的响应创建验证管道。
我也尝试过使用验证模式。在这种情况下,我将创建一些检查规则,并将这些规则应用于东西以进行验证。然后,我有了一个验证器对象,该对象将采用所有规则并提示适当的输出。这使代码更整洁,重用更好,但是增加了一些复杂性,最终我没有使用它。它与代码库的其余部分不同,因此对同事来说是陌生的,因此我有很好的理由不使用它。