重定向和导航/转发有什么区别?何时使用它们?

35

JSF中导航的区别是什么?

FacesContext context = FacesContext.getCurrentInstance();
context.getApplication().getNavigationHandler().handleNavigation(context, null, url);

以及重定向

HttpServletResponse response = (HttpServletResponse) FacesContext.getCurrentInstance().getExternalContext().getResponse();
response.sendRedirect(url);

如何决定使用哪种方法?

导航的问题在于,除非将faces-redirect=true添加到导航URL的查询字符串中,否则页面URL不会更改。然而,在我的情况下,如果我想要重定向到一个非JSF页面(比如普通的HTML页面),添加faces-redirect=true会引发错误。

另一个选项是像BalusC在JSF 2.0 redirect error中建议的那样。

1个回答

84
首先,“重定向”这个术语在Web开发世界中是指发送一个空的HTTP响应给客户端,只包含一个“Location”头,其中包含客户端必须发送全新GET请求的新URL。基本上如下:
  • 客户端发送HTTP请求到“somepage.xhtml”。
  • 服务器返回带有“Location: newpage.xhtml”头的HTTP响应
  • 客户端发送HTTP请求到“newpage.xhtml”(在浏览器地址栏中反映!)
  • 服务器返回带有“newpage.xhtml”内容的HTTP响应。
您可以使用Web浏览器的内置/附加开发人员工具集来跟踪它。在Chrome / IE9 / Firebug中按F12并检查“网络”部分即可看到它。
JSF导航处理程序不会发送重定向。相反,它使用目标页面的内容作为HTTP响应。
  • 客户端发送HTTP请求到“somepage.xhtml”。
  • 服务器返回带有“newpage.xhtml”内容的HTTP响应。
然而,由于最初的HTTP请求是针对somepage.xhtml的,因此浏览器地址栏中的URL保持不变。如果您熟悉基本Servlet API,那么您应该理解这与RequestDispatcher#forward()具有相同的效果。
关于是否从JSF中拉出HttpServletResponse并在其上调用sendRedirect()是正确的用法,不,这不是正确的用法。这样做会导致服务器日志充满IllegalStateException,因为您没有告诉JSF您已经接管了响应处理,并且JSF不应该执行其默认的响应处理工作。实际上,您应该在此之后执行FacesContext#responseComplete()
另外,每当您需要从javax.servlet.*包中导入内容到JSF构件(如托管的bean)中时,您绝对应停下来思考一下自己是否正在正确地编写代码,并问自己是否已经有了“标准JSF方式”来实现您想要实现的任何任务,或者是否该任务真的属于JSF托管的bean(有些情况下,一个简单的servlet filter可能更合适)。

在JSF中执行重定向的正确方式是在操作结果中使用faces-redirect=true查询字符串:

public String submit() {
    // ...
    return "/newpage.xhtml?faces-redirect=true";
}

如果你不在一个操作方法内,比如ajax或prerender监听器方法中,可以使用ExternalContext#redirect()进行重定向:

public void listener() throws IOException {
    // ...
    ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
    ec.redirect(ec.getRequestContextPath() + "/newpage.xhtml");
}

(是的,在IOException周围不需要放置try-catch,只需让异常通过throws传递,servlet容器将处理它)

或者在特定情况下使用NavigationHandler#handleNavigation(),如果您正在使用XML导航用例和/或带有一些内置侦听器的自定义导航处理程序:

public void listener() {
    // ...
    FacesContext fc = FacesContext.getCurrentInstance();
    NavigationHandler nh = fc.getApplication().getNavigationHandler();
    nh.handleNavigation(fc, null, "/newpage.xhtml?faces-redirect=true");
}

关于为什么导航处理程序无法处理“纯HTML”文件,那是因为导航处理程序只能处理JSF视图,而不能处理其他文件。你应该使用ExternalContext#redirect()
另请参阅:

感谢Balu详细的解释。我对JSF还很陌生,这有助于我理解其中的细微差别。我还参考了您在链接https://dev59.com/c2ox5IYBdhLWcg3wDgTR上的评论——添加一个空字符串确实会将我带回起始页面,但也会显示错误消息,指出找不到无效的处理程序。我不能在这里使用faces-redirect=true,因为我想返回一些消息到起始页面。有什么建议吗? - ad-inf
2
如果重定向在相同的路径上,那么您可以使用Flash#setKeepMessages()将所有的FacesMessage保留在flash范围内。 - BalusC
1
谢谢BalusC,你刚刚让我免于因Ajax而自杀。我在两个不同的项目中已经为无响应的按钮战斗了数月之久,当JSF启动新页面时,除非一个随机选择的命令按钮散布ajax =“false”,否则所有事件都将被预消耗。重新阅读您对此帖子的回复后,我想到尝试重定向以清除JSF中的表单工作区。它在页面和后备bean操作中都完美地工作了。缺点是我现在必须重新访问我的所有代码。 - Tim Maher-De Troyer
我认为这里有一个错别字:“客户端发送HTTP请求到somepage.xhtml。 服务器发送HTTP响应返回newpage.xhtml的内容。”你发送了请求到“Somepage.xhtml”,但得到了“newpage.xhtml”的内容, 所以,我是对的吗?还是我漏掉了什么 =? - Muntaser Abukhadijah
1
@Muntaser:没有错别字。语句“您发送了请求到“somepage.xhtml”,但得到了“newpage.xhtml”的内容”是正确的。如果您仍然不理解,请先退一步学习基本的Servlet,然后再深入研究JSF。 - BalusC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接