【问题标题】:JSF calls methods when managed bean constructor sends 404 ERROR CODEJSF 在托管 bean 构造函数发送 404 错误代码时调用方法
【发布时间】:2014-09-03 04:54:30
【问题描述】:

在 JSF 托管 bean 构造函数中,我使用请求参数从数据库加载实体。有时,实体不在数据库中,我想显示其他带有 404 消息的 JSF (.xhtml) 页面。
这是托管 bean 的示例:

@ManagedBean(name = "someBean")
@RequestScoped
public class SomeBean implements Serializable {

    private static final long serialVersionUID = 1L;

    private SomeData someData;

    public SomeBean() throws IOException {
        someData = ... loads from database using JPA features
        if(someData == null){
              HttpServletResponse response = (HttpServletResponse) FacesContext
                    .getCurrentInstance().getExternalContext().getResponse();
              response.sendError(404);
        }
    }

    public SomeData getSomeData(){
        return someData;
    }
}

我对 web.xml 文件进行了类似的配置:

<error-page>
   <error-code>404</error-code>
   <location>/404.xhtml</location>
</error-page>

我有一个 JSF 页面来处理托管 bean 加载的实体。当实体存在时,我将在页面中使用它。像这样:

<h1>#{someBean.someEntity.name}</h1>
<h2>#{someBean.someEntity.description}</h2>
<ui:repeat value="#{someBean.someEntity.books}" var="book">
// ..........
</ui:repeat>

当托管成功加载数据时,上面的页面工作。

问题

当实体不存在并且我发送一个 404 ERROR CODE 时,JSF 仍然处理在第一页的表达式语言中定义的方法。
此行为使托管 bean 抛出 NullPointerException 和 HTTP 500 错误代码。
我的 404 错误页面没有被调用。我不知道为什么。

即使在数据库中找到实体并且 404 错误页面正常工作,我也会尝试发送 404 错误。

谁能解释这种 JSF 的行为到这种幸福的程度?或者提供某种形式来显示 404 错误页面而不更改 URL?

【问题讨论】:

  • 什么服务器? FacesServlet 的 url 映射是什么?至少在 Wildfly 上似乎它在映射到 *.xhtml 时会起作用
  • 我使用 Tomcat 7、Servlet 3.0 API 和 JSF 2.2。其他 jsf 2 版本中也存在该问题。
  • 我使用 Tomcat 7、Servlet 3.0 API 和 JSF 2.2。该问题也出现在其他 jsf 2 版本中。我使用WELD CDI实现手册配置,但是没有WELD CDI的tomcat也出现这个问题。我尝试使用FacesContext.getCurrentInstance().responseComplete();,什么也没有。根据 JSF 生命周期,我认为这种行为是正确的,但我无法理解。
  • 我使用通常的 FacesServlet 映射 (/faces/*),使用 PrettyFaces 将 URL 映射到 .xhtml 文件;工作正常,除了报告的问题。

    没有 PrettyFaces 也会出现问题。

标签: jsf jsf-2


【解决方案1】:

您基本上是在渲染视图时尝试执行前端控制器逻辑。您应该渲染视图之前进行。因为,一旦您开始渲染视图,将视图更改为不同的目的地已经为时已晚,例如与您的情况一样的错误页面。您即不能从客户端取回已经发送的响应。

在 JSF 2.2 中,您可以为此使用 &lt;f:viewAction&gt;

<f:metadata>
    <f:viewAction action="#{bean.init}" />
</f:metadata>
public void init() {
    // ...

    if (someCondition) {
        context.getExternalContext().responseSendError(404, "some message");
        context.responseComplete();
    }
}

(请注意,当您需要将 javax.servlet.* 类导入 JSF 支持 bean 时,您绝对应该停下来看看 ExternalContext 中是否还没有该功能,否则请三思而后行。以正确的方式做事,例如,也许您需要一个 servlet 过滤器;还请注意,您需要明确告诉 JSF 您已完成响应,否则它仍会尝试呈现视图)

在 JSF 2.0/2.1 中,您可以为此使用 &lt;f:event type="preRenderView"&gt;。另请参阅What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?

如果您实际上是在尝试验证 HTTP 请求参数并且您也碰巧使用了 OmniFaces,您可以考虑将 &lt;f:viewParam&gt; 与真正的 JSF 验证器一起使用,并通过 OmniFaces &lt;o:viewParamValidationFailed&gt; 控制 sendError

【讨论】:

  • 我得到HTTP Status 500 - Cannot create a session after the response has been committed,用简单的f:viewAction在backing bean中调用一个init方法,其中init方法只调用...getExternalContext().responseSendError(404, null);
  • 我从 mojarra 看 jsf 的来源,responseSendError 看起来只是调用 HttpServletResponse.sendError,不像 OminiFaces 调用 responseComplete。那么,响应“已提交错误”的某些原因?
  • 对,另请参阅stackoverflow.com/questions/15796497/…。我会更新答案。
  • 我测试过并且工作得很好。但是,转换器/验证者是否负责执行此操作?我认为在验证器/转换器内部很难在响应中添加错误,这是出于责任。
  • 我会将转换器/验证器放在支持 bean 方法中。现在,将是惊人的。非常感谢
猜你喜欢
  • 1970-01-01
  • 2011-04-06
  • 2023-03-03
  • 2013-01-24
  • 2013-04-16
  • 1970-01-01
  • 2013-03-18
  • 2011-11-06
相关资源
最近更新 更多