Wicket 为什么在新标签页中打开 link 时页面会过期?
Wicket why page expires when opening link in new tab?
我正在构建具有以下规格的 wicket bootsrap 网络应用程序(来自 pom.xml):
检票口版本:6.15.0
wicket-bootstrap-core.version: 0.9.3-SNAPSHOT
我有一个基础页面,它是我其他页面的父页面,并在顶部添加了一个水平导航栏以标记其关键组件:
BootstrapBookmarkablePageLink 扩展 BookmarkablePageLink
这是我的一部分 BasePage.java
public abstract class BasePage extends GenericWebPage<Void> {
private static final long serialVersionUID = 1L;
String username;
public WicketApplication getApp() {
return WicketApplication.class.cast(getApplication());
}
public BasePage(final PageParameters parameters) {
super(parameters);
// Read session data
cachedUsername = (String)
BasicAuthenticationSession.get().getAttribute("username");
// create navbar
add(newNavbar("navbar"));
}
/**
* @return application properties
*/
public Properties getProperties() {
return WicketApplication.get().getProperties();
}
/**
* creates a new {@link Navbar} instance
*
* @param markupId
* The components markup id.
* @return a new {@link Navbar} instance
*/
protected Navbar newNavbar(String markupId) {
Navbar navbar = new Navbar(markupId) {
private static final long serialVersionUID = 1L;
@Override
protected TransparentWebMarkupContainer newCollapseContainer(String
componentId) {
TransparentWebMarkupContainer container =
super.newCollapseContainer(componentId);
container.add(new CssClassNameAppender("bs-navbar-collapse"));
return container;
}
};
navbar.setPosition(Navbar.Position.TOP);
// navbar.setInverted(true);
NavbarButton<Void> myTab = new NavbarButton<Void>(MyPage.class, new
PageParameters().add("name", "")
.add("status", "All").add("date", ""), Model.of("My page"));
NavbarButton<Void> myOtherTab = new NavbarButton<Void>
(MyOtherPage.class, new PageParameters().add("status", "initial")
.add("date", ""), Model.of("My other page"));
navbar.addComponents(NavbarComponents.transform(
Navbar.ComponentPosition.LEFT,
myTab, myOtherTab));
return navbar;
}
}
然后,MyPage 呈现一个过滤器表单,一个 html table,ajax 按钮和一些链接,我的一些组件是 ajax 个组件:
public class MyPage extends BasePage {
private static final long serialVersionUID = 5772520351966806522L;
@SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(MyPage.class);
private static final Integer DAYS = 270;
private DashboardFilteringPageForm filteringForm;
private CityInitialForm ncForm;
private String CityName;
private String startDate;
private CitysTablePanel citysTable;
private WebMarkupContainer numberOfNodes;
public MyPage(PageParameters parameters) throws ParseException {
super(parameters);
// get Citys list from repo
final List<City> repoCitys = (List<City>) methodToGetCities();
// select number of nodes
numberOfNodes = new WebMarkupContainer("numberOfNodes") {
private static final long serialVersionUID = 5772520351966806522L;
};
numberOfNodes.setOutputMarkupId(true);
ncForm = new CityInitialForm("ncForm");
// validation
add(new FeedbackPanel("feedbackPanel")).setOutputMarkupId(true);
ncForm.getNumberField().setRequired(true);
ncForm.add(new AjaxButton("ncButton") {
private static final long serialVersionUID = -6846211690328190809L;
@Override
protected void onInitialize() {
super.onInitialize();
add(newAjaxFormSubmitBehavior("change"));
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
// redirect to other page
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
numberOfNodes.add(ncForm);
// filters
CityName = parameters.get("name").toString() == null ? "" :
parameters.get("name").toString();
startDate = parameters.get("date").toString();
filteringForm = new DashboardFilteringPageForm("filteringForm") {
private static final long serialVersionUID = -1702151172272765464L;
};
// initialize form inputs
filteringForm.setCityName(CityName);
try {
filteringForm.setStartDate(new SimpleDateFormat("EE MMM dd HH:mm:ss
z yyyy", Locale.ENGLISH)
.parse(getStartDate().equals("") ?
CortexWebUtil.subtractDays(new Date(), DAYS).toString() : getStartDate()));
} catch (Exception e) {
setResponsePage(SignInPage.class, new PageParameters());
}
filteringForm.add(new AjaxButton("button") {
private static final long serialVersionUID = -6846211690328190809L;
@Override
protected void onInitialize() {
super.onInitialize();
add(newAjaxFormSubmitBehavior("change"));
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> paForm) {
// retrieve Citys
filterCitysAjax(target, "All");
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
filteringForm.getCityNameTextField().add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = 1468056167693038096L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
try {
filterCitysAjax(target, "All");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
// new City link
AjaxLink<Void> newCityLink = newCityLink("newCity", repoCitys);
// Citys table
citysTable = new CitysTablePanel("CitysTable", repoCitys);
citysTable.setOutputMarkupId(true);
// add components
add(filteringForm, newCityLink, numberOfNodes, citysTable);
}
private void filterCitysAjax(AjaxRequestTarget target, String status) {
methodToFilterResults();
// re-render table component
CitysTablePanel cityTableNew = new CitysTablePanel("CitysTable", citys);
cityTableNew.setOutputMarkupId(true);
cityTableNew.setVisibilityAllowed(true);
cityTableNew.setVisible(true);
citysTable.replaceWith(cityTableNew);
target.add(cityTableNew);
citysTable = cityTableNew;
target.appendJavaScript(CortexWebUtil.TABLE_ODD_EVEN_ROWS);
}
private AjaxLink<Void> newCityLink(String string, final List<City> Citys) {
final AjaxLink<Void> newCityLink = new AjaxLink<Void>(string) {
private static final long serialVersionUID = -5420108740617806989L;
@Override
public void onClick(final AjaxRequestTarget target) {
numberOfNodes.add(new AttributeModifier("style",
"display:block"));
target.add(numberOfNodes);
}
};
// new City image
Image newCityImage = new Image("newCityIcon", new
ContextRelativeResource("/img/new_City_icon.png"));
add(newCityLink);
newCityLink.add(newCityImage);
return newCityLink;
}
}
所以 MyPage 有效,但是当我在新选项卡中打开 MyOtherPage Link 并触发 MyPage 中的 ajax 组件(例如 AjaxButton)时,我收到页面过期错误。
为什么会这样?
我需要使用无状态页面吗? ( stateless link )
为什么在 wicket 中打开新选项卡中的链接并使用 ajax 组件如此困难?我一定是漏了什么..
以下是一些可能的原因:
MyPage 序列化失败
Wicket 将有状态页面存储在页面存储中(默认情况下在磁盘中)。稍后当您单击有状态 link Wicket 尝试加载页面。首先,它查看 http 会话,其中页面以其实时形式保存(即未序列化)。如果在那里找不到,则 Wicket 会在磁盘中查找。
Wicket 仅保留在 Http 会话中最后一个用户请求中使用的页面(以保持内存占用小)。通过单击 MyOtherPage link,您将 MyOtherPage 的实例放入 Http 会话中,而旧实例(MyPage 的)仅在磁盘中。 But:如果 MyPage 无法序列化为 byte[],则它无法存储在磁盘中,因此以后的请求将因 PageExpiredException 而失败。
待办事项:检查您的日志是否有 NotSerializableException 以及原因的良好调试消息。
MyOtherPage 太大
默认情况下,Wicket 在磁盘中为每个用户会话最多写入 10M。如果 MyPage 是 2M,MyOtherPage 是 9M(两种尺寸都很大,但我不知道您的应用程序会发生什么......)然后保存 MyOtherPage 将从磁盘中删除 MyPage。稍后尝试加载 MyPage 将失败并返回 PageExpiredException。
待办事项:查看 Wicket 模型的使用情况。
我正在构建具有以下规格的 wicket bootsrap 网络应用程序(来自 pom.xml):
检票口版本:6.15.0
wicket-bootstrap-core.version: 0.9.3-SNAPSHOT
我有一个基础页面,它是我其他页面的父页面,并在顶部添加了一个水平导航栏以标记其关键组件:
BootstrapBookmarkablePageLink 扩展 BookmarkablePageLink
这是我的一部分 BasePage.java
public abstract class BasePage extends GenericWebPage<Void> {
private static final long serialVersionUID = 1L;
String username;
public WicketApplication getApp() {
return WicketApplication.class.cast(getApplication());
}
public BasePage(final PageParameters parameters) {
super(parameters);
// Read session data
cachedUsername = (String)
BasicAuthenticationSession.get().getAttribute("username");
// create navbar
add(newNavbar("navbar"));
}
/**
* @return application properties
*/
public Properties getProperties() {
return WicketApplication.get().getProperties();
}
/**
* creates a new {@link Navbar} instance
*
* @param markupId
* The components markup id.
* @return a new {@link Navbar} instance
*/
protected Navbar newNavbar(String markupId) {
Navbar navbar = new Navbar(markupId) {
private static final long serialVersionUID = 1L;
@Override
protected TransparentWebMarkupContainer newCollapseContainer(String
componentId) {
TransparentWebMarkupContainer container =
super.newCollapseContainer(componentId);
container.add(new CssClassNameAppender("bs-navbar-collapse"));
return container;
}
};
navbar.setPosition(Navbar.Position.TOP);
// navbar.setInverted(true);
NavbarButton<Void> myTab = new NavbarButton<Void>(MyPage.class, new
PageParameters().add("name", "")
.add("status", "All").add("date", ""), Model.of("My page"));
NavbarButton<Void> myOtherTab = new NavbarButton<Void>
(MyOtherPage.class, new PageParameters().add("status", "initial")
.add("date", ""), Model.of("My other page"));
navbar.addComponents(NavbarComponents.transform(
Navbar.ComponentPosition.LEFT,
myTab, myOtherTab));
return navbar;
}
}
然后,MyPage 呈现一个过滤器表单,一个 html table,ajax 按钮和一些链接,我的一些组件是 ajax 个组件:
public class MyPage extends BasePage {
private static final long serialVersionUID = 5772520351966806522L;
@SuppressWarnings("unused")
private static final Logger LOG = LoggerFactory.getLogger(MyPage.class);
private static final Integer DAYS = 270;
private DashboardFilteringPageForm filteringForm;
private CityInitialForm ncForm;
private String CityName;
private String startDate;
private CitysTablePanel citysTable;
private WebMarkupContainer numberOfNodes;
public MyPage(PageParameters parameters) throws ParseException {
super(parameters);
// get Citys list from repo
final List<City> repoCitys = (List<City>) methodToGetCities();
// select number of nodes
numberOfNodes = new WebMarkupContainer("numberOfNodes") {
private static final long serialVersionUID = 5772520351966806522L;
};
numberOfNodes.setOutputMarkupId(true);
ncForm = new CityInitialForm("ncForm");
// validation
add(new FeedbackPanel("feedbackPanel")).setOutputMarkupId(true);
ncForm.getNumberField().setRequired(true);
ncForm.add(new AjaxButton("ncButton") {
private static final long serialVersionUID = -6846211690328190809L;
@Override
protected void onInitialize() {
super.onInitialize();
add(newAjaxFormSubmitBehavior("change"));
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
// redirect to other page
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
numberOfNodes.add(ncForm);
// filters
CityName = parameters.get("name").toString() == null ? "" :
parameters.get("name").toString();
startDate = parameters.get("date").toString();
filteringForm = new DashboardFilteringPageForm("filteringForm") {
private static final long serialVersionUID = -1702151172272765464L;
};
// initialize form inputs
filteringForm.setCityName(CityName);
try {
filteringForm.setStartDate(new SimpleDateFormat("EE MMM dd HH:mm:ss
z yyyy", Locale.ENGLISH)
.parse(getStartDate().equals("") ?
CortexWebUtil.subtractDays(new Date(), DAYS).toString() : getStartDate()));
} catch (Exception e) {
setResponsePage(SignInPage.class, new PageParameters());
}
filteringForm.add(new AjaxButton("button") {
private static final long serialVersionUID = -6846211690328190809L;
@Override
protected void onInitialize() {
super.onInitialize();
add(newAjaxFormSubmitBehavior("change"));
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> paForm) {
// retrieve Citys
filterCitysAjax(target, "All");
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
filteringForm.getCityNameTextField().add(new OnChangeAjaxBehavior() {
private static final long serialVersionUID = 1468056167693038096L;
@Override
protected void onUpdate(AjaxRequestTarget target) {
try {
filterCitysAjax(target, "All");
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
@Override
protected void updateAjaxAttributes(AjaxRequestAttributes
attributes) {
super.updateAjaxAttributes(attributes);
attributes.getAjaxCallListeners().add(new
DisableComponentListener(citysTable));
}
});
// new City link
AjaxLink<Void> newCityLink = newCityLink("newCity", repoCitys);
// Citys table
citysTable = new CitysTablePanel("CitysTable", repoCitys);
citysTable.setOutputMarkupId(true);
// add components
add(filteringForm, newCityLink, numberOfNodes, citysTable);
}
private void filterCitysAjax(AjaxRequestTarget target, String status) {
methodToFilterResults();
// re-render table component
CitysTablePanel cityTableNew = new CitysTablePanel("CitysTable", citys);
cityTableNew.setOutputMarkupId(true);
cityTableNew.setVisibilityAllowed(true);
cityTableNew.setVisible(true);
citysTable.replaceWith(cityTableNew);
target.add(cityTableNew);
citysTable = cityTableNew;
target.appendJavaScript(CortexWebUtil.TABLE_ODD_EVEN_ROWS);
}
private AjaxLink<Void> newCityLink(String string, final List<City> Citys) {
final AjaxLink<Void> newCityLink = new AjaxLink<Void>(string) {
private static final long serialVersionUID = -5420108740617806989L;
@Override
public void onClick(final AjaxRequestTarget target) {
numberOfNodes.add(new AttributeModifier("style",
"display:block"));
target.add(numberOfNodes);
}
};
// new City image
Image newCityImage = new Image("newCityIcon", new
ContextRelativeResource("/img/new_City_icon.png"));
add(newCityLink);
newCityLink.add(newCityImage);
return newCityLink;
}
}
所以 MyPage 有效,但是当我在新选项卡中打开 MyOtherPage Link 并触发 MyPage 中的 ajax 组件(例如 AjaxButton)时,我收到页面过期错误。
为什么会这样? 我需要使用无状态页面吗? ( stateless link )
为什么在 wicket 中打开新选项卡中的链接并使用 ajax 组件如此困难?我一定是漏了什么..
以下是一些可能的原因:
MyPage 序列化失败
Wicket 将有状态页面存储在页面存储中(默认情况下在磁盘中)。稍后当您单击有状态 link Wicket 尝试加载页面。首先,它查看 http 会话,其中页面以其实时形式保存(即未序列化)。如果在那里找不到,则 Wicket 会在磁盘中查找。 Wicket 仅保留在 Http 会话中最后一个用户请求中使用的页面(以保持内存占用小)。通过单击 MyOtherPage link,您将 MyOtherPage 的实例放入 Http 会话中,而旧实例(MyPage 的)仅在磁盘中。 But:如果 MyPage 无法序列化为 byte[],则它无法存储在磁盘中,因此以后的请求将因 PageExpiredException 而失败。
待办事项:检查您的日志是否有 NotSerializableException 以及原因的良好调试消息。
MyOtherPage 太大
默认情况下,Wicket 在磁盘中为每个用户会话最多写入 10M。如果 MyPage 是 2M,MyOtherPage 是 9M(两种尺寸都很大,但我不知道您的应用程序会发生什么......)然后保存 MyOtherPage 将从磁盘中删除 MyPage。稍后尝试加载 MyPage 将失败并返回 PageExpiredException。
待办事项:查看 Wicket 模型的使用情况。