控制器可以捕获模型抛出的异常吗?

Posted

技术标签:

【中文标题】控制器可以捕获模型抛出的异常吗?【英文标题】:Can a controller catch an exception thrown from a model? 【发布时间】:2012-12-22 21:26:06 【问题描述】:

嗯,这在技术上是可行的,但这会破坏 MVC 架构吗?

我不确定是否建议在控制器和模型之间进行这种类型的通信。我将使用一个简单的示例和两种方法来描述它:

选项 1(模型抛出异常并且控制器捕获它):

class Controller 
  private $model;

  public function save($data) 
     try 
         $this->model->save($data); 
      catch (Exception $e)   
         // handle exception
     
  


class Model 
  public function save($data) 
     // Call to internal function to save data in BD
     if (! $this->_save($data)) throw new Exception('Error saving data');
  

选项 2(控制器完全处理异常):

class Controller 
  private $model;

  public function save($data) 
     try 
         if (! $this->model->save($data)) throw new Exception('Error saving data'); 
      catch (Exception $e)  
         // handle exception
     
  


class Model 
  public function save($data) 
     // Call to internal function to save data in BD
     if (! $this->_save($data)) return false;
  

**

在一些回应后编辑:

**

这些是根据您的建议解决问题的其他方法。我希望不要把事情弄得太复杂。

选项 3(模型完全处理异常,正如 Ray 所说。KingCrunch 还建议在模型中更好地做到这一点)

class Controller 
  private $model;

  public function save($data) 

     if (! $this->model->save($data)) 
         // possible action: redirect to the form with an error message
     

  


class Model 
  public function save($data) 
     try 
         if (! $this->_save($data)) throw new Exception('Error saving data'); 
      catch (Exception $e)  
         // handle exception
         return false;
     
     return true; 
  

选项 4(控制器获取模型抛出的自定义子异常,如 shiplu.mokadd.im 所说。)

class Controller 
  private $model;

  public function save($data) 
     try 
         $this->model->save($data); 
      catch (Exception $e)   
         if ($e instanceof ValidationException) 
            // handle validation error
         
         elseif ($e instanceof DBStorageException) 
            // handle DB error
         
     
  


class Model 
  public function save($data) 
     if (! $this->_validate($data)) 
        throw new ValidationException ('Validation error');
     
     if (! $this->_save($data)) 
        throw new DBStorageException ('Storage error');
     
  

【问题讨论】:

为什么要在模型中抛出异常?这是为了验证吗? 这是我描述的选择之一。我试图简短,但它可以是数据验证、数据库存储,或两者兼而有之。 【参考方案1】:

模型可以抛出异常,控制器或视图应该捕获它。否则你永远不知道那里的一切是否正常。所以使用第一个选项。但请确保您抛出对控制器和视图有意义的正确抽象异常

要说明上面的粗线,请参见模型内部使用的这两个 throw 语句。

 throw new Exception('SQL Error: '.$mysqli->error()); // dont use it
 throw new DuplicateFieldException('Duplicate username'); // use this

第二个例子没有显示内部错误。相反,它隐藏了它。控制器永远不应该知道里面发生了什么。

在您的代码中,您将单个模型绑定到单个控制器。控制器不代表单个模型。它使用模型。它可以使用任意数量的模型。因此,不要将单个模型与带有 private $model 之类的变量的控制器绑定在一起。

【讨论】:

嗯,这是一个简短的例子。我并没有试图关注控制器可以处理多少个模型。只是他们之间的交流。我试图简明扼要。 :) 你确定吗?我可以实现一个应用程序,其中我只需要当前控制器的模型。在这种情况下,一个指向模型实例的变量就足够了。 是的,我确定。控制器不应与单个模型捆绑在一起。控制器不是模型接口。 这个编辑的行让我有点困惑:确保你抛出对控制器和视图有意义的正确抽象异常。能详细解释一下吗? 我在答案中添加了更多示例。【参考方案2】:

绝对是第一选择。一些话:

控制器的工作是……嗯,控制。这意味着,它应该小心,至少会出现一个有用的错误消息。应用程序的其他部分可能会在能够处理异常情况之前执行此操作。这包括模型本身:如果它能够处理它,它应该这样做。

save() 表示“保存”。不要滥用状态信息的返回值。当方法不能save() 时,它是一个例外,当方法不需要给你东西时,它不应该给你东西。

【讨论】:

所以,如果我没记错的话,在第一点你的意思是,只要模型本身不能处理异常(我理解这将是最好的方法),那么它应该回到控制器,并且应该在其中处理(捕获)异常,而模型仍然会抛出它。但是我知道我不应该依赖控制器的任何验证:例如,如果模型在数据库中保存数据失败,然后它用简单的 FALSE 通知控制器,然后控制器抛出并捕获,这是一个不好的做法例外。【参考方案3】:

我更喜欢选项 3。

模型应该捕获异常,尝试解决它,如果不将它渗透到控制器,但前提是它是控制器可以解决和恢复的东西。在这种情况下,(某种 DB 保存失败)在返回 false 的模型中捕获它应该足以解决保存错误,并为控制器提供足够的信息来知道保存时出了什么问题。

控制器不需要担心模型如何实现保存的实现细节。

【讨论】:

你好 Ray,我的例子的修改版本可以帮助我理解你的意思 :) 我刚刚扩展了这个问题,包括你的建议。 @LuisMartin 对于这个特定示例,我现在更喜欢#3(将模型保存到数据库中)有时你会合理地想向控制器抛出异常,我只是不认为这是一个 false 返回值不够的地方。 谢谢!现在我有一个两难选择:哪个答案标记为已接受。所有这些都很有帮助,但我不确定选择哪一个作为最好的。

以上是关于控制器可以捕获模型抛出的异常吗?的主要内容,如果未能解决你的问题,请参考以下文章

主线程会捕获另一个线程抛出的异常吗?

java中throw抛出的异常一定要用相应的catch块处理吗

搭建控制器时抛出的调用目标抛出异常

当容器管理的 tx EJB 提交时,如何捕获和包装 JTA 抛出的异常?

Spring中如何管理过滤器中抛出的异常?

Flutter:FireBase 抛出的 PlatformException 不会被捕获