使用PrimeExceptionHandler时WEB-INF下错误页面找不到问题排查
核心原因
- Servlet规范明确规定,
WEB-INF目录下的所有资源禁止客户端直接访问,所有指向/WEB-INF/*路径的客户端请求都会被容器直接拦截返回404,只有服务器端的内部转发、包含操作才能访问这个目录下的内容。 - 你配置的错误页路径指向WEB-INF下的xhtml文件时出现404,本质是当前异常处理流程没有走服务器内部转发,而是触发了客户端302重定向,让浏览器直接请求了WEB-INF下的地址,触发了容器的安全拦截。
- 之所以会触发不该有的重定向,绝大多数情况是你没有给
*.xhtml后缀绑定Faces Servlet映射。容器默认会对静态资源类型的错误页直接做内部转发,但如果目标错误页是需要经过动态Servlet解析的资源(比如JSF页面),又没有匹配到对应的Servlet处理逻辑,容器就会错误地走重定向逻辑,最终把WEB-INF的路径暴露给客户端。 - 你把错误页移到webapp根目录能正常运行,是因为根目录下的资源没有访问限制,就算触发重定向,浏览器请求
/expired.xhtml时不会被容器拦截,同时如果这个路径匹配了Faces Servlet的映射,就能正常渲染出页面。
修复步骤
按以下顺序调整配置即可让WEB-INF下的错误页正常生效:
- 先确认
web.xml中Faces Servlet的映射规则覆盖所有xhtml文件,保证内部转发到xhtml页面时能被JSF引擎正常解析,参考配置如下:
<servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.xhtml</url-pattern> </servlet-mapping>
- 确认错误页的配置路径写的是绝对上下文路径,也就是你最开始写的
/WEB-INF/errorpages/expired.xhtml格式,开头的斜杠不能丢。 - 如果调整完上述配置还是出现跳转到WEB-INF路径报404的问题,可以加一个自定义异常过滤器,在过滤器里捕获
ViewExpiredException,手动执行服务器内部转发到错误页,完全绕开容器默认的错误处理逻辑,核心逻辑参考:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { try { chain.doFilter(req, res); } catch (Exception e) { Throwable rootCause = getRootCause(e); if (rootCause instanceof ViewExpiredException) { req.getRequestDispatcher("/WEB-INF/errorpages/expired.xhtml").forward(req, res); return; } throw e; } }
- 额外注意:WEB-INF下的错误页里引用的CSS、JS、图片等静态资源不要写相对路径,要放在webapp根目录的静态资源文件夹下用绝对路径引用,不然转发后会出现资源加载失败的问题。
内容的提问来源于stack exchange,提问作者grigouille
相关产品推荐
相关产品推荐

