webapi MVC.net中的异常处理

时间:2016-11-10 09:20:06

标签: asp.net-mvc exception asp.net-web-api exception-handling controller

我有一个带控制器和服务的API,当调用控制器中的一个动作时我必须应用验证,这个验证需要检查数据库中的数据以验证是否正确。

据我所知,有两种方法可以处理这个

1-在调用"更新"之前验证防止错误

public IActionResult UpdateCustomer(CustomerDto customer)
{
   if (!customerService.Validate(customer))
   {
       return Send400BadRequest();
   }
   customerService.Update(customer);

   return Send200Ok();
}

2-调用内部更新验证并抛出异常。

 public IActionResult UpdateCustomer(CustomerDto customer)
 {
    customerService.Update(customer);    
    return Send200Ok();
 }

在客户服务中

  public void Update(CustomerDto customer)
  {
     if (!Validate(customer)
        throws new CustomValidationException("Customer is not valid");
     //Apply update
  }

我们已经有一个ActionFilter来处理 CustomValidationException ,所以它会返回一个BadRequest。

1) 优点 +不要使用Exception来使用正在运行的流程

缺点 - 控制器更胖,对每种情况都有更多的决定它将决定哪个是输出

2) 优点 +操作更原子,所有逻辑都在服务中。 +更容易测试 +将验证该方法的每次使用。

缺点 - 使用例外来管理流程。

哪种解决方案更好?

我真的需要争论来保护其中一方。

7 个答案:

答案 0 :(得分:3)

如果您有业务逻辑层服务层,我更愿意保留所有业务逻辑规则,包括业务逻辑业务逻辑层中的验证,并使用服务层作为业务逻辑层的包装,并将<{1}}投入< strong>商业方法。

在决定是否对业务验证规则使用例外时,您可以考虑:

1)您的业务方法最好是工作单元。他们应该完成一项完整的任务。因此,它们还包含验证规则。这样,您可以跨不同的服务层重用此类业务逻辑层,或者在同一的不同方法中使用相同的工作单元服务层。如果您在业务逻辑层中抛出业务验证异常,您将不会面临忘记验证或错误使用其他验证规则的风险,并且每个服务方法/操作将为您执行单个任务,并且将尽可能轻量级。 / p>

考虑何时可能需要为某些客户端公开WCF服务,或者例如,如果您可以在不使用WebAPI的情况下使用ASP.NET MVC,或者如果您想在同一WebAPI中的另一个Action方法中使用相同的逻辑。

如果您在Web API控制器中放置业务逻辑验证,在创建WCF服务方法或创建MVC操作或其他服务方法时,您可能会忘记应用验证或可能在不同的规则中应用错误的验证新的服务层

2)考虑到第一个好处,您是否可以从显示成功,失败或包含有关输出中失败原因的适当信息的方法中返回有意义的值?

我认为对所有这些目标使用out out方法并不合适。方法输出是方法输出,它应该是这种业务应用程序中的数据。它有时不应该是状态,有时是数据或某些时候的消息。抛出异常将解决这个问题。

答案 1 :(得分:1)

我反对这里的其他意见,并说第一种方法都清楚地说明了你的方法的意图是什么,如果你决定不返回400错误,它更容易在情景中拉出来#1。

另外,关于例外的一些想法。例外情况应该是例外,这意味着您的代码中会发生意外事件。没有通过验证检查的公司不是异常事件,它要么通过要么不通过。将User对象传递给ValidateCompany()方法应该抛出异常。

Here是关于异常抛出的类似主题的一个很好的答案。它使用一个简单的示例问题来确定何时应该抛出异常。

关于“更容易测试” - 我不知道如何。您的控制器将对您选择的任何选项,有效公司和无效公司进行两次测试。您的服务将有两个测试,包括您选择的任何选项,一个有效的公司和一个无效的公司(显然在这里简化您的服务层)。在任何情况下,您都希望确保控制器操作和服务层都能处理无效且有效的公司对象。

答案 2 :(得分:0)

我更喜欢2:)

因为我认为可以从另一个节点调用服务,而不仅仅是asp.net控制器,所以如果所有验证逻辑都在像服务层这样的单层处理,那么对我来说会很好。

答案 3 :(得分:0)

我认为使用httpresponse消息处理异常比其他任何人好得多。至少你得到了正确的错误响应,正确的http响应消息作为输出。

      public HttpResponseMessage TestException()
      {
        try
        {
            //if your code works well
            return Request.CreateResponse(HttpStatusCode.OK);
        }
        catch (Exception ex)
        {

            return  Request.CreateErrorResponse(HttpStatusCode.ExpectationFailed, ex.ToString());
        }
      }

答案 4 :(得分:0)

我会这样做:

public IActionResult UpdateCustomer(CustomerDto customer)
    {
    try
        {
        consumerService.Update (customer);
        }
    catch (CustomValidationException)
        {
        return Send400BadRequest ();
        }

    return Send200Ok ();
    }

在您的CustomerService中:

   public void Update(CustomerDto customer)     
       {
       if (!Validate(customer))
           throw new CustomValidationException("Customer is not valid");
       }

这样您的服务就具有验证逻辑。因此,您的服务的任何其他呼叫者也将在尝试更新之前验证其输入,因此将获得正确的错误消息。 您的控制器将没有任何验证码。而且,拥有try-catch块意味着您可以优雅地处理Update调用可能抛出的任何其他错误。希望有所帮助。

答案 5 :(得分:0)

  

接收可验证的客户模型,将此模型传递到数据服务层,然后执行异常   如果数据服务层失败,则在控制器中进行处理。

客户模式

实施IValidatableObject以捕获与业务逻辑相关的错误。

public class CustomerModel : IValidatableObject
{
    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // example validation
        if ( Balance < 100 )
            yield return new ValidationResult("Balance >= 100 req.", new [] { nameof(Balance) });
    }
    public string Name { get; set; }
    public double Balance { get; set; }
}

API控制器

在面向公众的CustomerModel API方法中接收UpdateCustomer,然后参考ModelState以确定客户是否有效。

public IActionResult UpdateCustomer(CustomerModel customer)
{
    if(ModelState.IsValid) // model validation succeeded
    {
        try
        {
            customerService.Update(customer);
        }
        catch (ServiceUpdateException svcEx)
        {
            // handled failure at the service layer
            return Conflict();
        }
        catch (Exception ex)
        {
            // unhandled error occured
            return InternalServerError(ex);
        }
        // update succeeded
        return Ok();
    }
    // invalid model state
    return BadRequest();
}

服务(数据层)

public void Update(Customer customer)
{
     //Apply update
     try
     {
         database.Update(customer);
     }
     catch (DataStorageException dbEx)
     {
         throw new ServiceUpdateException(dbEx);
     }
     catch
     {
         throw;//unknown exception
     }
}

答案 6 :(得分:0)

我认为您应该在中验证控制器和服务,但验证稍有不同的东西。特别是如果开始只是一个api变成api,经常发生mvc网站和wpf管理界面。

使用Validation Attributes和模型绑定可快速提供数据&#34;一旦结束&#34;校验。他们是否提供了客户更新的ID?他们是否为相关实体提交了外键价值?价格介于0到1,000,000之间吗?如果您有一个网站,那么您也可以立即获得客户验证。至关重要的是根本不需要连接到数据库,因为数据显然是#34;错。

还需要在服务中进行验证,因为您可能最终会调用3或4个调用此服务的应用程序,并且您希望编写一次业务逻辑。一些验证,例如。引用外键只能在数据库中完成,但抛出异常没有错。您的api的消费者应该可以访问有效值的范围,网站的用户应该从下拉菜单等中进行选择,因此这种情况应该特殊