wsaca
wsaca

Reputation: 55

Redirect after JSF Ajax (ViewRoot) update

I'm using omnifaces FullAjaxExceptionHandler to redirect when any exception is fired with ajax request, in my case to caught ViewExpiredException.

I'll try to explain my case:

1) I have the index.xhtml page with a commandButton (ajax request) to invalidate the session and return an action like this "/login?faces-redirect=true".

2) Open index.xhtml two times in the same browser.

3) In the first index.xhtml click in the button (the session is invalidate and redirected to login.xhtml)

In this point take in account I have a custom PhaseListener to check if the user is sucessfully logged, login.xhtml is for public access but index.xhtml is restricted for authorized users, web.xml for view expired and 403 error is:

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/index.xhtml</location>
</error-page>
<error-page>
    <error-code>403</error-code>
    <location>/403.xhtml</location>
</error-page>

4) Go to the second index.xhtml (session is invalidated in this moment), now click in the button to launch the exception.

FullAjaxExceptionHandler catch the ViewExpiredException and try to render index.xhtml, but in the same time my phaselistener is checking if the user is authorized, like the session was invalidated, the user is not authorized and my PhaseListener fire responseSendError(403), but is not showing 403.xhtml, because is an ajax request.

How I can send an 403 error to show 403.xhtml from my PhaseListener? responseSendError is cancelling the normal process for FullAjaxExceptionHandler, in this moment when that exception happens no redirection take place.

In addition, to clarify when the view expire I need redirect to index.xhtml because in production I'm using a CAS (Central Authentication Service).

Upvotes: 1

Views: 1715

Answers (1)

BalusC
BalusC

Reputation: 1108852

The concrete problem is in essence unrelated to the FullAjaxExceptionHandler. You'd still have had exactly the same problem when not using it. It's not possible to use responseSendError() on ajax requests for at least 2 reasons:

  1. The ajax response must have status 200 in order to be successfully processed.
  2. The ajax response must represent a valid JSF ajax XML structure, not a plain HTML page.

Inside a PhaseListener, your best bet is to use ExternalContext#redirect().

ExternalContext ec = context.getExternalContext();

if (context.getPartialViewContext().isAjaxRequest()) {
    ec.redirect(ec.getRequestContextPath() + "/403.xhtml");
} else {
    ec.responseSendError(403, "Unauthorized");
}

True, this ends up in a HTTP 200 response, but that's so far the best you can get when you want to show the error page in its entirety in case of an ajax request.


Unrelated to the concrete problem, if a ViewExpiredException is been thrown, then the cause is in almost all cases an expired session. It would make more sense to just let the exception's error page point to /login.xhtml directly instead of /index.xhtml. Or, at least more user friendly, to some /expired.xhtml page with therein an explanation why the enduser ended up in there and what his options are, along with some links.

Upvotes: 2

Related Questions