Зачем использовать перенаправление JSF ExceptionHandlerFactory вместо перенаправления <error-page>?

Все примеры ExceptionHandlerFactory, с которыми я столкнулся, перенаправляют пользователя на страницу viewExpired.jsf в случае обнаружения ViewExpiredException:

public class ViewExpiredExceptionExceptionHandler extends ExceptionHandlerWrapper {
    private ExceptionHandler wrapped;

    public ViewExpiredExceptionExceptionHandler(ExceptionHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ExceptionHandler getWrapped() {
        return this.wrapped;
    }

    @Override
    public void handle() throws FacesException {
        for (Iterator<ExceptionQueuedEvent> i = getUnhandledExceptionQueuedEvents().iterator(); i.hasNext();) {
            ExceptionQueuedEvent event = i.next();
            ExceptionQueuedEventContext context = (ExceptionQueuedEventContext) event.getSource();

            Throwable t = context.getException();
            if (t instanceof ViewExpiredException) {
                ViewExpiredException vee = (ViewExpiredException) t;
                FacesContext facesContext = FacesContext.getCurrentInstance();
                Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
                NavigationHandler navigationHandler = facesContext.getApplication().getNavigationHandler();
                try {
                    // Push some useful stuff to the request scope for use in the page
                    requestMap.put("currentViewId", vee.getViewId());
                    navigationHandler.handleNavigation(facesContext, null, "/viewExpired");
                    facesContext.renderResponse();
                } finally {
                    i.remove();
                }
            }
        }

        // At this point, the queue will not contain any ViewExpiredEvents. Therefore, let the parent handle them.
        getWrapped().handle();
    }
}

Мне кажется, что следующая простая конфигурация web.xml принципиально такая же и намного проще:

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/viewExpired.jsf</location>
</error-page>

Это вызывает вопрос: зачем использовать ExceptionHandlerFactory?

Ответ 1

В конкретном примере реализована только одна полезная вещь: она сохраняет идентификатор вида как атрибут запроса, так что вы можете использовать, например,

<h:link value="Go back to previous page" outcome="#{currentViewId}" />

Но это не очень полезно, поскольку URI необработанного запроса уже доступен атрибутом запроса <error-page> по умолчанию javax.servlet.error.request_uri.

<h:outputLink value="#{requestScope['javax.servlet.error.request_uri']}">Go back to previous page</h:outputLink>

Однако одна вещь, для которой пользовательский ExceptionHandler действительно полезен, заключается в том, что он позволяет вам обрабатывать исключения во время ajax-запросов. По умолчанию у них нет единственной формы полезной обратной связи на стороне клиента. Только в Mojarra с этапом проекта, установленным в "Development", вы увидите сообщение с предупреждением о появлении JavaScript с сообщением об исключении. Но это так. На этапе "Производство" нет единой формы обратной связи. С помощью настраиваемого ExceptionHandler вы сможете проанализировать web.xml, чтобы найти местоположения страниц ошибок, создать с ним новый UIViewRoot и заставить JSF установить ajax-рендеринг на @all.

Итак, в основном:

String errorPageLocation = "/WEB-INF/errorpages/500.xhtml";
context.setViewRoot(context.getApplication().getViewHandler().createView(context, errorPageLocation));
context.getPartialViewContext().setRenderAll(true);
context.renderResponse();

См. также этот связанный вопрос: Каков правильный способ справиться с исключениями JSF 2.0 для компонентов AJAXified? и этот блог: Полный обработчик исключений Ajax.

Ответ 2

Это зависит от того, что вы хотите сделать, когда будете получать ViewExpiredException.

Если вы просто хотите отобразить страницу пользовательской ошибки, вы можете сделать это, как вы сказали.

Этот post показывает вам, как программно перехватывать ViewExpiredException и сделайте с ним что-нибудь хорошее.