控制器可以捕获模型抛出的异常吗?
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块处理吗