Error handling 如何处理Wicket自定义模型中抛出的异常?

Error handling 如何处理Wicket自定义模型中抛出的异常?,error-handling,wicket,Error Handling,Wicket,我有一个带有自定义模型的组件(扩展wicket标准模型类)。当Wicket调用getObject()时,我的模型从数据库/web服务加载数据 由于以下几个原因,此查找可能会失败。我想通过在带有组件的网页上显示一条漂亮的消息来处理这个错误。最好的方法是什么 public class MyCustomModel extends Model { @Override public String getObject() { try { return

我有一个带有自定义模型的组件(扩展wicket标准模型类)。当Wicket调用
getObject()
时,我的模型从数据库/web服务加载数据

由于以下几个原因,此查找可能会失败。我想通过在带有组件的网页上显示一条漂亮的消息来处理这个错误。最好的方法是什么

public class MyCustomModel extends Model {

    @Override
    public String getObject() {
        try {
            return Order.lookupOrderDataFromRemoteService();
        } catch (Exception e) {
            logger.error("Failed silently...");
            // How do I propagate this to the component/page?
        }           
        return null;
}

请注意,错误发生在与组件分离的模型内部。

我会在页面中添加一个反馈面板,并在catch子句中调用错误(“某些描述”)。

这里的主要问题是,
模型通过设计与组件层次结构分离,您可以实现一个组件感知的
模型
,该模型将针对特定组件报告所有错误

请记住,要确保它实现了可拆卸的
,以便相关的
组件将被分离

如果
Model
将执行代价高昂的操作,您可能会对使用
LoadableDetachableModel
感兴趣(考虑到
Model.getObject()
可能会被多次调用)


也值得尝试一下
会话。get().error())

您的模型可以实现IComponentAssignedModel,从而能够保留所拥有的组件

但我想知道您多久能重用一次MyCustomModel?
我知道一些开发人员提倡创建独立的模型实现(通常在单独的包中)。虽然这在一般情况下是有用的(例如FeedbackMessagesModel),但根据我的经验,创建特定于组件的内部类更容易。

您可能只需要在
getObject
中返回null,并在
getObject
返回null时向控制器类添加逻辑以显示消息

如果由于不同的失败原因需要自定义消息,可以添加一个属性,如
stringerrormessage
到在
getObject
中捕获异常时设置的模型,因此您的控制器类可以执行如下操作

if(model.getObject == null) {
 add(new Label("label",model.getErrorMessage()));
} else {
 /* display your model object*/
}

处理模型的getObject()中发生的异常非常棘手,因为此时我们通常已经深入到整个请求周期的响应阶段,更改组件层次结构为时已晚。因此,处理异常的唯一地方是非常非本地的,不是在组件或模型附近,而是在
RequestCycle

尽管如此,还是有办法解决的。我们结合使用
行为
IRequestCycleListener
来处理此问题:

  • IRequestCycleListener#OneException
    允许您检查请求期间引发的任何异常。如果从该方法返回一个
    IRequestHandler
    ,则将运行并呈现该处理程序,而不是之前发生的任何事情

    我们单独使用它来捕获通用的东西,比如Hibernate的
    StaleObjectException
    ,将用户重定向到通用的“其他人修改了您的对象”页面。如果你

  • 对于更具体的情况,我们添加了
    RuntimeExceptionHandler
    行为:

    public abstract class RuntimeExceptionHandler extends Behavior {
        public abstract IRequestHandler handleRuntimeException(Component component, Exception ex);
    }
    
    IRequestCycleListener
    中,我们遍历当前页面的组件树,查看是否有任何组件具有
    RuntimeExceptionHandler
    的实例。如果我们找到一个,我们调用它的
    handleRuntimeException
    方法,如果它返回一个
    IRequestHandler
    ,我们将使用它。通过这种方式,您可以将错误的实际处理放在页面的本地

    例如:

    public MyPage() {
      ...
      this.add(new RuntimeExceptionHandler() {
        @Override public IRequestHandler handleRuntimeException(Component component, Exception ex) {
          if (ex instanceof MySpecialException) {
            // just an example, you really can do anything you want here.
            // show a feedback message...
            MyPage.this.error("something went wrong"); 
            // then hide the affected component(s) so the error doesn't happen again...
            myComponentWithErrorInModel.setVisible(false); // ...
            // ...then finally just re-render this page:
            return new RenderPageRequestHandler(new PageProvider(MyPage.this));
          } else {
            return null;
          }
        }
      });
    }
    
    注意:这不是Wicket附带的产品,我们自己推出。我们简单地结合了Wicket的
    IRequestCycleListener
    Behavior
    特性,得出了这个结论


鉴于
模型
与组件层次结构分离,您如何从
模型
调用
组件#错误
?使用
Session.get().error()
maybe?@bert:这就是问题所在:您无法访问modelArrgh中的组件层次结构。很抱歉,没有提到这一点,因为我从未从模型中调用服务。+1用于教我有关
IComponentAssignedModel
。这正是我写那个答案时所想的。很高兴看到Wicket对于几乎任何用例都有一个完美的解决方案。我们有一些带有额外逻辑的模型类,可以在所有地方重用。它们还可以从一个页面传递到另一个页面,这对于匿名/内部类是不推荐的。而且,即使在一个页面中,您通常也可以在多个组件(如标签和formcomponent)之间共享模型。或显示同一对象不同属性的多个面板。对于这种用例,IComponentAssignedModel是合适的。这里有很多很好的答案,但这抓住了要点:在模型中添加对组件的引用。在“我们浏览当前页面的组件树”之前,您一直在问我这是怎么做的?我现在不知道语法,但您可以使用visit()页对象上的方法。您向它传递一个IVisitor,它迭代组件,并将每个组件传递给您。然后可以检查每个组件上的行为,以查看是否找到RuntimeExceptionHandler。
public MyPage() {
  ...
  this.add(new RuntimeExceptionHandler() {
    @Override public IRequestHandler handleRuntimeException(Component component, Exception ex) {
      if (ex instanceof MySpecialException) {
        // just an example, you really can do anything you want here.
        // show a feedback message...
        MyPage.this.error("something went wrong"); 
        // then hide the affected component(s) so the error doesn't happen again...
        myComponentWithErrorInModel.setVisible(false); // ...
        // ...then finally just re-render this page:
        return new RenderPageRequestHandler(new PageProvider(MyPage.this));
      } else {
        return null;
      }
    }
  });
}