JSF中如何根据PROJECT_STAGE配置不同错误页面?开发显异常栈,生产显自定义消息
Absolutely! There are a couple of clean, straightforward ways to implement this requirement in JSF, leveraging the built-in PROJECT_STAGE context parameter and JSF's native error handling tools. Let me break down the most practical approaches for you:
1. Unified Error Page with EL Conditionals (Simplest Approach)
This method uses a single error page that dynamically shows content based on the current PROJECT_STAGE using JSF EL expressions. It’s perfect if you don’t need complex exception processing.
Step 1: Configure Global Error Page in web.xml
First, tell your servlet container to route all unhandled exceptions to a single error page:
<error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error.xhtml</location> </error-page>
Step 2: Create the Dynamic Error Page (error.xhtml)
In this page, use #{facesContext.application.projectStage} to check if you’re in Development or Production mode, then render the appropriate content. Servlet containers expose error-related details in the request scope, which you can access to show stack traces in development:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets"> <h:head> <title>Error Occurred</title> </h:head> <h:body> <!-- Development Environment: Show full exception details --> <ui:fragment rendered="#{facesContext.application.projectStage eq 'Development'}"> <h1>Development Environment Error</h1> <div style="margin: 20px 0;"> <h3>Exception Type:</h3> <p>#{requestScope['javax.servlet.error.exception_type']}</p> </div> <div style="margin: 20px 0;"> <h3>Exception Message:</h3> <p>#{requestScope['javax.servlet.error.message']}</p> </div> <div style="margin: 20px 0;"> <h3>Stack Trace:</h3> <pre style="background: #f5f5f5; padding: 15px; border-radius: 4px;">#{requestScope['javax.servlet.error.stack_trace']}</pre> </div> <div style="margin: 20px 0;"> <h3>Request URI:</h3> <p>#{requestScope['javax.servlet.error.request_uri']}</p> </div> </ui:fragment> <!-- Production Environment: Show user-friendly message --> <ui:fragment rendered="#{facesContext.application.projectStage eq 'Production'}"> <h1>Oops! Something Went Wrong</h1> <p style="font-size: 1.1em; margin: 20px 0;"> We’re sorry, but an unexpected issue has occurred. Our team has been notified and is working to fix it right away. </p> <p style="font-size: 1.1em;"> Please try again later, or contact our support team if the problem persists. </p> </ui:fragment> </h:body> </html>
2. Custom Exception Handler (Advanced, Flexible Control)
If you need more control—like logging exceptions in production, redirecting to separate error pages, or handling specific exception types differently—use a custom ExceptionHandler.
Step 1: Create the Exception Handler Wrapper
This class checks the PROJECT_STAGE and handles exceptions accordingly:
import javax.faces.application.ProjectStage; import javax.faces.context.ExceptionHandler; import javax.faces.context.ExceptionHandlerWrapper; import javax.faces.context.FacesContext; import javax.faces.event.ExceptionQueuedEvent; import java.util.Iterator; public class StageAwareExceptionHandler extends ExceptionHandlerWrapper { private final ExceptionHandler wrapped; public StageAwareExceptionHandler(ExceptionHandler wrapped) { this.wrapped = wrapped; } @Override public ExceptionHandler getWrapped() { return wrapped; } @Override public void handle() { FacesContext context = FacesContext.getCurrentInstance(); boolean isDevMode = ProjectStage.Development.equals(context.getApplication().getProjectStage()); Iterator<ExceptionQueuedEvent> exceptionEvents = getUnhandledExceptionQueuedEvents().iterator(); while (exceptionEvents.hasNext()) { ExceptionQueuedEvent event = exceptionEvents.next(); Throwable exception = event.getContext().getException(); if (isDevMode) { // In dev mode, print stack trace to console (JSF may also show it in the browser) exception.printStackTrace(); } else { // In production, log the exception (use your preferred logging framework like SLF4J) // Logger.getLogger(getClass()).error("Unhandled exception in production", exception); // Redirect to a production-specific error page try { context.getExternalContext().redirect("/error-production.xhtml"); context.renderResponse(); // Signal JSF to skip rendering the original view } catch (Exception e) { // Fallback if redirect fails e.printStackTrace(); } } exceptionEvents.remove(); } // Let the default handler process any remaining exceptions getWrapped().handle(); } }
Step 2: Create an Exception Handler Factory
Register your custom handler with JSF using a factory:
import javax.faces.context.ExceptionHandler; import javax.faces.context.ExceptionHandlerFactory; public class CustomExceptionHandlerFactory extends ExceptionHandlerFactory { private final ExceptionHandlerFactory parentFactory; public CustomExceptionHandlerFactory(ExceptionHandlerFactory parentFactory) { this.parentFactory = parentFactory; } @Override public ExceptionHandler getExceptionHandler() { return new StageAwareExceptionHandler(parentFactory.getExceptionHandler()); } }
Step 3: Configure the Factory in faces-config.xml
Add this entry to register your custom exception handling:
<factory> <exception-handler-factory>com.yourpackage.CustomExceptionHandlerFactory</exception-handler-factory> </factory>
Key Notes
- Ensure your
web.xmlhas the correctPROJECT_STAGEcontext parameter—JSF automatically detects this value and makes it available viafacesContext.application.projectStage. - In development mode, JSF’s default behavior already shows detailed errors, but using a custom error page lets you format the output to your liking.
- Always avoid exposing sensitive exception details (like stack traces) in production—both methods above ensure this is handled properly.
内容的提问来源于stack exchange,提问作者Nitin Kolte

