我为JSF编写了一个自定义异常处理程序,用于记录异常并导航到要显示给用户的错误页面。不幸的是,当调用Handler中的"handleNavigation"时,我得到一个IllegalStateException"在响应被提交后不能调用sendRedirect()"。知道我哪里做错了吗?
我的处理程序:
public class MyExceptionHandler extends ExceptionHandlerWrapper {
private Logger log = ...;
public ExceptionHandler wrappedHandler = null;
public MyExceptionHandler (ExceptionHandler wrappedHandler) {
this.wrappedHandler = wrappedHandler;
}
@Override
public ExceptionHandler getWrapped() {
return wrappedHandler;
}
@Override
public void handle() throws FacesException {
Iterator<ExceptionQueuedEvent> iter = null;
ExceptionQueuedEvent event = null;
ExceptionQueuedEventContext eventContext = null;
FacesContext facesContext = null;
iter = getUnhandledExceptionQueuedEvents().iterator();
while (iter.hasNext()) {
try {
event = iter.next();
eventContext = (ExceptionQueuedEventContext) event.getSource();
log.error("JSF Exception aufgetreten", eventContext.getException());
facesContext = FacesContext.getCurrentInstance();
// !!!!!!!! Exception occurs here !!!!!!!!!!!
facesContext
.getApplication()
.getNavigationHandler()
.handleNavigation(facesContext, null, "error");
facesContext.renderResponse();
} catch (RuntimeException ex) {
throw ex; // just to set break point
} finally {
iter.remove();
}
}
getWrapped().handle();
}
}
faces-config.xml中的导航定义
<navigation-rule>
<from-view-id>*</from-view-id>
<navigation-case>
<from-outcome>error</from-outcome>
<to-view-id>/faces/error.xhtml</to-view-id>
<redirect/>
</navigation-case>
</navigation-rule>
正在处理的异常显然是在呈现响应阶段抛出的,此时HTTP响应已经提交。已提交的响应意味着HTTP响应的第一部分(包括报头)已经被发送到客户端。这是一个没有退路的时刻。您不能从客户端取回已发送的字节。
当写入的内容超过缓冲区大小时,响应通常会自动提交,缓冲区大小默认为2KB,这取决于servletcontainer和Facelets的配置。HTML <head>
平均占用1~2KB。所以变化很大,在JSF开始渲染<body>
之前,响应就已经提交了,或者只是其中的一小部分。
在你的特殊的情况下,然而,还有一个原因:当你收到多个未处理的异常时,那么你也会遇到麻烦,因为你没有在导航后中止while
循环,而是继续处理下一个异常。您不能为单个请求返回多个响应(错误页面)。您应该收集所有异常并只导航一次,或者在第一个异常之后终止循环。
在任何情况下,处理在渲染响应期间抛出的异常只有在响应没有(自动)提交的情况下才有可能。您可以尝试几种方法来防止响应过早地自动提交:
-
将Facelets缓冲区大小设置为最大的HTML响应大小。例如64 kb:
<context-param> <param-name>javax.faces.FACELETS_BUFFER_SIZE</param-name> <param-value>65535</param-value><!-- 64KB --> </context-param>
-
在呈现视图之前执行异常敏感的业务作业(即不要在GET请求期间构造的bean的(post)构造函数中执行):
<f:event type="preRenderView" listener="#{bean.init}" />
一般来说,要处理JSF中的异常,您可能会发现OmniFaces FullAjaxExceptionHandler
很有帮助。你可以在这里找到源代码。
参见:
- 在JSF 2.1中@PostConstruct抛出的异常导致IllegalStateException
- 在web.xml中定义的错误页面嵌入到部分呈现的JSF页面