C# 我们在哪里处理ASP.NET WebAPI中的错误,维护精简控制器和关注点分离?
因此,当我使用WebAPI并试图维护精简控制器时,我试图找出在哪里处理异常 因此,一个简单的例子是通过电子邮件检索用户。我的API需要首先验证用户是否存在。如果不是,则需要返回404 为了维护瘦控制器,我只想将请求传递给某个地方的服务。我希望避免将存储库注入控制器。现在,服务部门负责首先检查用户是否存在。如果是,则返回用户。我感到困惑的是当用户不存在时 当用户不存在时,服务是否有责任抛出HttpException?如果是这样的话,该服务将不会被视为域服务,但可能是一个特殊的rest验证服务?如果是这样,它是否抓取用户,然后在用户确实存在时将用户传递给域服务,以避免多次调用数据存储 如果有一个更复杂的场景,我可能想在购物车中添加一个项目,那该怎么办。控制器将post请求移交给正确的服务。然后,该服务将验证一些业务规则:购物车存在、最大订单未超过等 这是两种不同类型的验证。将它们分离到单独的验证服务中感觉是对的,否则我最终会将域验证与rest验证混合在一起 我想我是想弄清楚如何保持关注点的速度,并将域验证转换为rest验证C# 我们在哪里处理ASP.NET WebAPI中的错误,维护精简控制器和关注点分离?,c#,rest,validation,architecture,asp.net-web-api2,C#,Rest,Validation,Architecture,Asp.net Web Api2,因此,当我使用WebAPI并试图维护精简控制器时,我试图找出在哪里处理异常 因此,一个简单的例子是通过电子邮件检索用户。我的API需要首先验证用户是否存在。如果不是,则需要返回404 为了维护瘦控制器,我只想将请求传递给某个地方的服务。我希望避免将存储库注入控制器。现在,服务部门负责首先检查用户是否存在。如果是,则返回用户。我感到困惑的是当用户不存在时 当用户不存在时,服务是否有责任抛出HttpException?如果是这样的话,该服务将不会被视为域服务,但可能是一个特殊的rest验证服务?如果
如果有人能给我一些启示或给我指出正确的方向,那就太好了 在这种情况下,我的一般选择是在服务/存储库中使用特定于域的异常,例如
UserNotFoundException
。我认为该服务干预web容器的动态不是一个好主意。您可能希望为控制台应用程序重用相同的服务,等等
然后,您可能会捕获此特定于域的异常并生成404页面。我同意你的选择,让它远离控制器。可以为异常指定错误处理程序。有不同级别的错误处理程序。您可以为每个控制器定义一个控制器,也可以为整个应用程序定义一个控制器。或者,您可以使用注释来标记将使用错误处理程序的控制器操作
我认为购物车案例也支持这种设计。您可以使用验证服务验证购物车。如果验证未成功,则购物车服务抛出ValidationException
。您的错误处理程序可能会捕获ValidationException
,并生成相应的响应代码
错误处理程序的一个优点是,有时更改需求会迫使您在发生特定错误时运行某些业务逻辑。如果您将错误处理隔离到错误处理程序,那么就很容易操作错误处理的逻辑。这是一个非常好的问题,我为您将存储库排除在控制器之外而鼓掌,这太好了。您还应该将HttpStatus代码排除在服务之外。这是我推荐的模式 API控制器 把他们想象成“交通警察”,他们把交通导向不同的服务,这就是他们所做的。它们不应该包含业务逻辑。例如:
[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);
}
[HttpGet]
[路由(“api/v1/codes/uidRequest/{uidRequestKey}”)]
[响应类型(类型(UIDRequestUIDsModel))]
公共异步任务视图UID(字符串uidRequestKey)
{
var uidRequestKeyGuid=uidRequestKey.ToNullableGuid();
如果(uidRequestKeyGuid==null)
{
返回BadRequest($“{uidRequestKey}不是有效的guid”);
}
如果(!ModelState.IsValid)
{
返回请求(ModelState);
}
var validatedResult=await _uidRequestApiService.GetUIDRequestUIDsAsync(uidRequestKeyGuid.Value,CurrentUserIdentity);
返回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));
}
受保护的IHttpActionResult ParseValidatedResult(ValidatedResult apiResult),其中TApiModel:class
{
如果(apiResult==null)
{
返回NotFound();
}
if(apireult.Success)
{
返回Ok(apiResult.Result);
}
如果(!apireult.Success&&apireult.FailureReason==ValidationFailureReason.Unauthorized)
{
返回状态码(HttpStatusCode.禁止);
}
返回BadRequest(string.Join(“\n”,apireult.Errors));
}
希望这会有所帮助。可能也是基于意见的,但当然服务不会抛出任何HTTP异常。它将返回控制器的信息,控制器响应HTTP。否则你不能在其他地方使用该服务,分离会非常人工Hanks,这很有帮助。我想这篇文章可能会对你有所帮助:这是我最初的想法,但我扮演了魔鬼代言人:有没有更好的模式可以减少创建特定异常类的开销。我现在倾向于认为这是必要的。我已经创建了验证服务,因此这些服务抛出的特定异常类似乎是公开失败的验证的合乎逻辑的方式。