JSF/PrimeFaces ajax 请求上的会话超时和 ViewExpiredException 处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11203195/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Session timeout and ViewExpiredException handling on JSF/PrimeFaces ajax request
提问by Mark Estrada
I find this article to be useful for non-ajax request How to handle session expiration and ViewExpiredException in JSF 2?but I can't make use of this when I am submitting using an AJAX call.
我发现这篇文章对非 ajax 请求很有用如何在 JSF 2 中处理会话过期和 ViewExpiredException?但是当我使用 AJAX 调用提交时,我无法使用它。
Suppose in a primefaces dialog, I am making a post request using AJAX and session has already timed out. I see my page getting stuck.
假设在一个primefaces 对话框中,我正在使用AJAX 发出一个post 请求并且会话已经超时。我看到我的页面卡住了。
How to fix this kind of scenario such that when I post using AJAX, I could redirect him to my view expired page and then forward him to the login page similar to the solution in the link above?
如何解决这种情况,以便当我使用 AJAX 发帖时,我可以将他重定向到我的查看过期页面,然后将他转发到类似于上面链接中的解决方案的登录页面?
JSF2/Primefaces/Glassfish
JSF2/Primefaces/Glassfish
回答by BalusC
Exceptions which are thrown during ajax requests have by default totally no feedback in the client side. Only when you run Mojarra with project stage set to Developmentand use <f:ajax>, then you will get a bare JavaScript alert with the exception type and message. But other than that, and in PrimeFaces, there's by default no feedback at all. You can however see the exception in the server log and in the ajax response (in the webbrowser's developer toolset's "Network" section).
默认情况下,ajax 请求期间抛出的异常在客户端完全没有反馈。只有当您在项目阶段设置为Development并使用的情况下运行 Mojarra 时<f:ajax>,您才会收到带有异常类型和消息的纯 JavaScript 警报。但除此之外,在 PrimeFaces 中,默认情况下根本没有反馈。但是,您可以在服务器日志和 ajax 响应(在 webbrowser 的开发人员工具集的“网络”部分)中看到异常。
You need to implement a custom ExceptionHandlerwhich does basically the following job when there's a ViewExpiredExceptionin the queue:
您需要实现一个自定义ExceptionHandler,当ViewExpiredException队列中有 a 时,它基本上完成以下工作:
String errorPageLocation = "/WEB-INF/errorpages/expired.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();
Alternatively, you could use the JSF utility library OmniFaces. It has a FullAjaxExceptionHandlerfor exactly this purpose (source code here, showcase demo here).
或者,您可以使用 JSF 实用程序库OmniFaces。它有一个FullAjaxExceptionHandler正是为了这个目的(源代码在这里,展示演示在这里)。
See also:
也可以看看:
回答by Douglas Nassif Roma Junior
A merge between the answer of @BalusC and this post, I solved my problem!
@BalusC 的答案和这篇文章的合并,我解决了我的问题!
My ExceptionHandlerWrapper:
我的 ExceptionHandlerWrapper:
public class CustomExceptionHandler extends ExceptionHandlerWrapper {
private ExceptionHandler wrapped;
CustomExceptionHandler(ExceptionHandler exception) {
this.wrapped = exception;
}
@Override
public ExceptionHandler getWrapped() {
return wrapped;
}
@Override
public void handle() throws FacesException {
final Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator();
while (i.hasNext()) {
ExceptionQueuedEvent event = i.next();
ExceptionQueuedEventContext context
= (ExceptionQueuedEventContext) event.getSource();
// get the exception from context
Throwable t = context.getException();
final FacesContext fc = FacesContext.getCurrentInstance();
final Map<String, Object> requestMap = fc.getExternalContext().getRequestMap();
final NavigationHandler nav = fc.getApplication().getNavigationHandler();
//here you do what ever you want with exception
try {
//log error ?
//log.log(Level.SEVERE, "Critical Exception!", t);
if (t instanceof ViewExpiredException) {
requestMap.put("javax.servlet.error.message", "Session expired, try again!");
String errorPageLocation = "/erro.xhtml";
fc.setViewRoot(fc.getApplication().getViewHandler().createView(fc, errorPageLocation));
fc.getPartialViewContext().setRenderAll(true);
fc.renderResponse();
} else {
//redirect error page
requestMap.put("javax.servlet.error.message", t.getMessage());
nav.handleNavigation(fc, null, "/erro.xhtml");
}
fc.renderResponse();
// remove the comment below if you want to report the error in a jsf error message
//JsfUtil.addErrorMessage(t.getMessage());
} finally {
//remove it from queue
i.remove();
}
}
//parent hanle
getWrapped().handle();
}
}
My ExceptionHandlerFactory:
我的 ExceptionHandlerFactory:
public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory {
private ExceptionHandlerFactory parent;
// this injection handles jsf
public CustomExceptionHandlerFactory(ExceptionHandlerFactory parent) {
this.parent = parent;
}
@Override
public ExceptionHandler getExceptionHandler() {
ExceptionHandler handler = new CustomExceptionHandler(parent.getExceptionHandler());
return handler;
}
}
My faces-config.xml
我的faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config version="2.2"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd">
<factory>
<exception-handler-factory>
your.package.here.CustomExceptionHandlerFactory
</exception-handler-factory>
</factory>
</faces-config>
回答by RajV
I am using Mojarra 2.1.7 in Production mode with JBoss 7. After the session expires, AJAX calls return an error XML document. You can easily catch this error using the usual onerror handler of f:ajax.
我在 JBoss 7 的生产模式下使用 Mojarra 2.1.7。会话到期后,AJAX 调用返回错误 XML 文档。您可以使用 f:ajax 的常用 onerror 处理程序轻松捕获此错误。
<script type="text/javascript">
function showError(data) {
alert("An error happened");
console.log(data);
}
</script>
<h:commandLink action="...">
<f:ajax execute="..." render="..." onerror="showError"/>
</h:commandLink>
回答by Ramya
I have included this in my ViewExpiredExceptionHandler class and it worked fine for me in WAS
我已经将它包含在我的 ViewExpiredExceptionHandler 类中,它在 WAS 中对我来说很好用
public void handle() throws FacesException {
FacesContext facesContext = FacesContext.getCurrentInstance();
for (Iterator<ExceptionQueuedEvent> iter = getUnhandledExceptionQueuedEvents()
.iterator(); iter.hasNext();) {
Throwable exception = iter.next().getContext().getException();
if (exception instanceof ViewExpiredException) {
final ExternalContext externalContext = facesContext
.getExternalContext();
try {
facesContext.setViewRoot(facesContext.getApplication()
.getViewHandler()
.createView(facesContext, "/Login.xhtml")); //Login.xhtml is the page to to be viewed. Better not to give /WEB-INF/Login.xhtml
externalContext.redirect("ibm_security_logout?logoutExitPage=/Login.xhtml"); // when browser back button is pressed after session timeout, I used this.
facesContext.getPartialViewContext().setRenderAll(true);
facesContext.renderResponse();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
iter.remove();
}
}
}
getWrapped().handle();
}
Hope this helps
希望这可以帮助
回答by Rami
I faced this problem, Requirement need to display a confirmation popup when user do any action after session gets timed out, my proposed solution was:
我遇到了这个问题,需要在会话超时后用户执行任何操作时显示确认弹出窗口,我建议的解决方案是:
<security:http use-expressions="true" auto-config="true" entry-point-ref="authenticationEntryPoint">
<security:intercept-url pattern="/common/auth/**" access="permitAll" />
<security:intercept-url pattern="/javax.faces.resource/**" access="permitAll" />
<security:intercept-url pattern="/**/ *.*" access="hasRole('ROLE_ADMIN')" />
<security:form-login login-page="/common/auth/login.jsf" />
<!-- <security:remember-me key="secret" services-ref="rememberMeServices" /> -->
<security:logout invalidate-session="true" logout-success-url="/common/auth/login.jsf" />
</security:http>
<bean id="authenticationEntryPoint" class="com.x.y.MyRedirectEntryPoint" >
<property name="loginFormUrl" value="/common/auth/login.jsf"/>
</bean>
The MyRedirectEntryPoint should extends AuthenticationProcessingFilterEntryPoint and override commence method
MyRedirectEntryPoint 应扩展 AuthenticationProcessingFilterEntryPoint 并覆盖开始方法
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException)
throws IOException, ServletException {
boolean ajaxRedirect = request.getHeader("faces-request") != null
&& request.getHeader("faces-request").toLowerCase().indexOf("ajax") > -1;
if (ajaxRedirect) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
response.sendError(403);
}
} else {
super.commence(request, response, authException);
}
}
Now you can simply bind a callback javascript function to catch the thrown 403 error and do what ever you want:
现在您可以简单地绑定一个回调 javascript 函数来捕获抛出的 403 错误并执行您想要的任何操作:
$(document).bind('ajaxError',
function(event, request, settings, exception){
if (request.status==403){
//do whatever you wanted may be show a popup or just redirect
window.location = '#{request.contextPath}/';
}
});
回答by mko
For me a simple client side javascript handler worked:
对我来说,一个简单的客户端 javascript 处理程序起作用了:
function handleAjaxExpired(xhr,status,args) {
// handler for "oncomplete" ajax callback
if ( xhr.responseXML.getElementsByTagName('error-name').length ) {
// "<error-name>" tag is present -> check for "view expired" exception
html = xhr.responseXML.getElementsByTagName('error-name')[0].innerHTML;
if ( html.indexOf('ViewExpiredException') > -1 ) {
// view expired exception thrown
// do something / reload page
if ( confirm('session expired -> reload page?') ) {
document.location.href=document.location.href;
}
}
}
}
This handler is called from "oncomplete" attribute in triggering UI element, e.g. here from a rowSelect event in a Primefaces datatable:
这个处理程序是从触发 UI 元素中的“oncomplete”属性调用的,例如在这里从 Primefaces 数据表中的 rowSelect 事件调用:
<p:ajax event="rowSelect" oncomplete="handleAjaxExpired(xhr,status,args)" />
Update:To avoid adding "oncomplete" attributes to every ajax-enabled element, this javascript code searches globally in all ajax responses for errors:
更新:为了避免向每个启用 ajax 的元素添加“oncomplete”属性,此 javascript 代码在所有 ajax 响应中全局搜索错误:
(function() {
// intercept all ajax requests
var origXHROpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener('load', function() {
handleAjaxExpired(this);
});
origXHROpen.apply(this, arguments);
};
})();
This code makes "oncomplete" attributes in PrimeFaces UI-elements obsolete.
此代码使 PrimeFaces UI 元素中的“oncomplete”属性过时。

