Wicket 6:过期后重新创建页面时清空 PageParameters

Wicket 6: empty PageParameters when recreating a page after expiration

我们遇到了 Wicket 6(即版本 6.22.0)的问题。看起来这是这里修复的:https://issues.apache.org/jira/browse/WICKET-5068 简而言之:页面过期后,Wicket 尝试通过以页面 class 和 PageParameters 作为参数调用构造函数来重建它,但是 PageParameters 是(错误地)空的,即使一些参数随请求一起发送。

After Wicket session timeout - pageParameters are null 似乎与同一问题有关。

WICKET-5068 修复了 Wicket 7,但我们有 Wicket 6,我们需要修复它。

以下是对我们的发现和一些问题的详细解释。

事情是这样的:

  1. 用户打开一个页面(它是有状态的)并在浏览器选项卡中保持打开状态。
  2. 用户打开其他页面
  3. 步骤 1 中的原始页面已从页面存储中逐出(即过期),尽管会话仍在进行中。
  4. 用户 returns 到初始浏览器选项卡并单击 link。这是 link:

    的代码
    AjaxLink<Void> link = new AjaxLink<Void>("link") {
        @Override
        public void onClick(AjaxRequestTarget target) {
            showWindow(dataModel, window, target);
        }
    };
    add(link);
    
  5. 虽然 BookmarkableMapper 根据请求构建 IRequestHandler,但会调用以下方法 (AbstractBookmarkableMapper:294):

    protected PageParameters getPageParametersForListener(PageInfo pageInfo, PageParameters pageParameters)
    {
        if (pageInfo.getPageId() != null)
        {
            // WICKET-4594 - ignore the parsed parameters for stateful pages
            return null;
        }
        return pageParameters;
    }
    

    因此根据请求构建的 ListenerInterfaceRequestHandler 具有 null for PageParameters

  6. Wicket 开始处理点击。它尝试恢复被点击的 link 所属的页面,这是通过以下方法完成的(PageProvider,从第 252 行开始):

    private void resolvePageInstance(Integer pageId, Class<? extends IRequestablePage> pageClass,
        PageParameters pageParameters, Integer renderCount)
    {
        IRequestablePage page = null;
    
        boolean freshCreated = false;
    
        if (pageId != null)
        {
            page = getStoredPage(pageId);
        }
    
        if (page == null)
        {
            if (pageClass != null)
            {
                page = getPageSource().newPageInstance(pageClass, pageParameters);
                freshCreated = true;
            }
        }
    
        if (page != null && !freshCreated)
        {
            if (renderCount != null && page.getRenderCount() != renderCount)
            {
                throw new StalePageException(page);
            }
        }
    
        pageInstanceIsFresh = freshCreated;
        pageInstance = page;
    }
    

    当页面从页面存储中被逐出时,以下语句的条件成立:

    if (page == null)
    

    因此它尝试从 class 和页面参数创建页面实例:

    page = getPageSource().newPageInstance(pageClass, pageParameters);
    

    但是 pageParameters 在这里是 null(因为第 5 项中的 getPageParametersForListener())。所以页面构造函数变得空 PageParameters 并且失败,因为它需要一些 id.

这是在页面构造函数中从 PageParameters 中提取 id 的代码:

pageParameters.get("id").toLong()

这是生成的异常(仅显示最上面的几行,其余部分无关紧要):

org.apache.wicket.util.string.StringValueConversionException: Unable to convert 'null' to a long value
    at org.apache.wicket.util.string.StringValue.toLong(StringValue.java:664)

所以在我们的例子中 getPageParametersForListener() 方法打破了恢复过期页面处理的可能性。

为了解决这个问题,我们用自定义实现替换了 BookmarkableMapper

public class BookmarkableMapperThatSavesPageParametersForListener extends BookmarkableMapper {
    @Override
    protected PageParameters getPageParametersForListener(PageInfo pageInfo, PageParameters pageParameters) {
        return pageParameters;
    }
}

我们在WebApplication#init()方法中挂载:

mount(new BookmarkableMapperThatSavesPageParametersForListener());

它似乎解决了我们面临的问题:link 单击不会触发处理程序(onClick() 方法),但至少页面不会爆炸,只会自行刷新。

问题是

  1. 发生这种情况是因为我们做错了什么还是 Wicket 中的错误?
  2. 我们应用的修复是否符合条件?我想 https://issues.apache.org/jira/browse/WICKET-4594 引入的更改并非只是为了好玩
  3. 知道我们只有有状态页面,我们的修复会破坏任何东西吗?

这是 Wicket 6.x 的限制,已在 7.x 中实施。 6.x 没有得到这个改变,因为我们不确定它是否会悄悄地破坏某人的应用程序。 IIRC 如果在升级期间需要,可以覆盖 7.x 中的方法以恢复到旧行为。 AFAIK 没有人抱怨 7.x 中的这一变化,所以我想将它移植到 6.x (6.27.0) 是可以的,但是 Wicket 的活跃开发者的 none 使用 6.x 并且有人这样做的机会相当低。 建议升级到7.x。它很稳定,有许多新功能和错误修复。 在那之前,我想您的选择是使用此请求映射器的自定义版本。