JSF 2.0 自定义异常处理程序
我正在努力完全理解 JSF 2.0 中何时/如何引发异常。我寻找解决方案的时间比我愿意承认的要长。最终,我想要实现的目标是“处理”未处理的异常。当引发异常时,我希望能够捕获有关异常的感兴趣信息,并将其通过电子邮件发送给相应的站点管理员。我通过在我的一个支持 bean 的构造函数中抛出一个 new FacesException() 来强制发生错误。我在 JSF 1.1 中使用 MyFaces 实现,效果很好。我可以通过包装默认生命周期并简单地覆盖execute() 和render() 方法来实现此工作。我按照 Hanspeter 的这篇很棒的帖子来实现它:
我现在正在使用 Mojarra 将站点升级到 JSF 2.0。然而,只要在execute() 方法中抛出/捕获异常,事情仍然会很好地工作;当我进入 render() 时,HttpServletResponse.isCommissed() 等于 true,并且阶段是 PhaseId RENDER_RESPONSE,这当然意味着我无法执行重定向或转发。我不明白 JSF 1.1 和 2.0 在何时/如何提交响应方面发生了什么变化。正如我所指出的,我在 1.1 框架中完美地工作了。 经过大量搜索后,我发现 JSF 2.0 通过自定义异常处理程序提供了一个很好的异常处理选项。我关注了 Ed Burns 的博客,在 JSF2 中处理 ViewExpiredException:
正如Ed指出的那样,通过定义标签总是有web.xml方式以及什么类型的异常/服务器错误代码以及希望将错误发送到哪个页面。只要我能捕获 404 错误,这种方法就非常有效。然而,需要注意的一件有趣的事情是,如果我通过键入不存在的 URL(例如 /myApp/9er)强制出现 404 错误,则错误处理程序会很好地工作,但是一旦我添加“.xhtml”扩展名(即 /myApp/ 9er.xhtml) 那么 web.xml 定义不会处理它。
我注意到 Ed 所做的一件我没有尝试过的事情是,他没有尝试执行 HttpServletRespone.sendRedirect(),而是利用 Navigationhandler.handleNavigation() 将用户转发到自定义错误页面。不幸的是,此方法与 Faclets 默认情况下对错误所做的操作没有任何不同。当然,除此之外,由于与上述相同的问题,我无法执行 HttpServletResponse.sendRedirect() ; response.isCommited() 等于 true。
我知道这篇文章会很长,所以我将快速记录一下尝试使用 PhaseListener 来实现相同的目的。我使用以下帖子作为指南,但这条路线仍然不成功:
- http://ovaraksin.blogspot.com/2010/10/global-handling-of-all-unchecked.html
- http://ovaraksin.blogspot.com/2010/10/jsf-ajax-redirect-after-session-timeout.html< /a>
所有我都遇到与已经提到的相同的问题。当引发此异常时,响应已处于提交阶段,并且我无法将用户重定向/转发到标准错误页面。
我对这么长的帖子表示歉意,我只是想提供尽可能多的信息以帮助消除歧义。任何人对解决方法有任何想法/想法,我很好奇 JSF 1.1 和 2.0 之间可能有什么不同,这会导致我进入生命周期的 render() 阶段时立即提交响应。
非常感谢您对此的任何帮助!
I’m struggling fully understanding when/how exceptions are thrown in JSF 2.0. I’ve looked for a solution longer than I care to admit. Ultimately, the goal I want to achieve is “handle” an unhandled exceptions. When an exception is thrown, I want to be able to capture information of interest about the exception, and email that to the appropriate site administrators. I’m forcing an error by throwing a new FacesException() in the constructor of one of my backing beans. I had this working great in JSF 1.1 using MyFaces implementation. I was able to get this working by wrapping the Default Lifecycle and simply overriding the execute() and render() methods. I followed this awesome post by Hanspeter to get that working:
I am now undergoing a site upgrade to JSF 2.0 using Mojarra’s. And things work great still as long as the exception is thrown/caught in the execute() method, however; the moment I enter the render(), the HttpServletResponse.isCommitted() equals true, and the phase is PhaseId RENDER_RESPONSE which of course means I can’t perform a redirect or forward. I don’t understand what has changed between JSF 1.1 and 2.0 in regards to when/how the response is committed. As I indicated, I had this working perfectly in the 1.1 framework.
After much searching I found that JSF 2.0 provides a great option for exception handling via a Custom ExceptionHandler. I followed Ed Burns’ blog, Dealing Gracefully with ViewExpiredException in JSF2:
As Ed indicates there is always the web.xml way by defining the tag and what type of exception/server error code and to what page one wants sent to for the error. This approach works great as long as I’m catching 404 errors. One interesting thing to note about that however, is if I force a 404 error by typing a non-exsitant URL like /myApp/9er the error handler works great, but as soon as I add “.xhtml” extension (i.e. /myApp/9er.xhtml) then the web.xml definition doesn’t handle it.
One thing I noticed Ed was doing that I hadn’t tried was instead of trying to do a HttpServletRespone.sendRedirect(), he is utilizing the Navigationhandler.handleNavigation() to forward the user to the custom error page. Unfortunately, this method didn’t do anything different than what Faclets does with the error by default. Along with that of course, I was unable to do HttpServletResponse.sendRedirect() due to the same problems as mentioned above; response.isCommitted() equals true.
I know this post is getting long so I will make a quick note about trying to use a PhaseListener for the same purposes. I used the following posts as a guide with this route still being unsuccessful:
- http://ovaraksin.blogspot.com/2010/10/global-handling-of-all-unchecked.html
- http://ovaraksin.blogspot.com/2010/10/jsf-ajax-redirect-after-session-timeout.html
All and all I have the same issues as already mentioned. When this exception is thrown, the response is already in the committed phase, and I’m unable to redirect/forward the user to a standard error page.
I apologize for such a long post, I’m just trying to give as much information as possible to help eliminate ambiguity. Anyone have any ideas/thoughts to a work around, and I’m curious what might be different between JSF 1.1 and 2.0 that would cause the response to be committed as soon as I enter the render() phase of the Lifecycle.
Thanks a ton for any help with this!!!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我已经使用如上所述的响应包装器成功实现了一个过滤器,它避免了提交响应,并允许重定向到自定义页面,即使在渲染页面过程中出现异常也是如此。
响应包装器在 StringWriter 上设置其自己的内部 PrintWriter,该 StringWriter 由 getWriter 方法返回,以便缓冲 faces 输出。在愉快的路径中,过滤器随后将内部 StringWriter 内容写入实际响应。出现异常时,过滤器会重定向到错误 jsp,该错误 jsp 会写入(尚未提交)响应。
对我来说,避免提交响应的关键是拦截flushBuffer()方法(来自ServletResponse,而不是HttpServletResponse),并避免调用super.flushBuffer()。我怀疑根据具体情况并且如上所述,可能还需要覆盖其他一些方法,例如设置标头的方法。
I have successfully implemented a filter using response wrapper as described above which avoids the response being commited and allows redirection to a custom page even on an exception in the middle of rendering the page.
The response wrapper sets up its own internal PrintWriter on a StringWriter, which is returned by the getWriter method so that the faces output is buffered. In the happy path, the filter subsequently writes the internal StringWriter contents to the actual response. On an exception, the filter redirects to an error jsp which writes to the (as yet uncommitted) response.
For me, the key to avoiding the response getting committed was to intercept the flushBuffer() method (from ServletResponse, not HttpServletResponse), and avoid calling super.flushBuffer(). I suspect that depending on circumstances and as noted above, it might also be necessary to also override some of the other methods, eg the ones that set headers.
所以这个问题实际上不仅仅是关于自定义异常处理程序(JSF 2 具有强大的 ExceptionHandlerFactory 机制),更多的是关于在响应已提交时向用户显示自定义错误页面。
即使最后一位已写入响应,始终能够重定向用户的一种通用方法是使用 HttpServletResponse 包装器来缓冲写入其中的标头和内容。
这确实会产生不利影响,即用户看不到逐渐构建的页面。
也许您可以使用此技术来仅捕获 JSF 2.0 似乎所做的非常早期的响应提交。一旦渲染响应开始,您就发出到目前为止缓冲的标头并直接写出响应内容。
这样,如果在渲染响应之前发生异常,您仍然可以将用户重定向到自定义错误页面。
So this question is actually not just about a custom exception handler (for which JSF 2 has the powerful
ExceptionHandlerFactory
mechanism), but more about showing the user a custom error page when the response has already been committed.One universal way to always be able to redirect the user even if the last bit has already been written to the response is using a
HttpServletResponse
wrapper that buffers headers and content being written to it.This does have the adverse effect that the user doesn't see the page being build up gradually.
Maybe you can use this technique to only capture the very early response commit that JSF 2.0 seems to do. As soon as render response starts, you emit the headers you buffered till so far and write out the response content directly.
This way you might still be able to redirect the user to a custom error page if the exception occurs before render response.