如何在不使用 HTML 表单的情况下以编程方式将 POST 请求发送到 JSF 页面?

Posted

技术标签:

【中文标题】如何在不使用 HTML 表单的情况下以编程方式将 POST 请求发送到 JSF 页面?【英文标题】:How to programmatically send POST request to JSF page without using HTML form? 【发布时间】:2019-08-01 06:22:49 【问题描述】:

我有一个非常简单的 JSF bean,如下所示:

import org.jboss.seam.annotations.Name;

@Name(Sample.NAME)
public class Sample 

    public static final String NAME="df";

    private String text = "text-test";

    public void sampleM()
        System.out.println("Test: "+text);
    

    public String getText() 
        return text;
    

    public void setText(String text) 
        this.text = text;
    

与此组件连接的 JSF 表单:

<h:form id="sampleForm">
        <h:commandButton id="sampleButton" action="#df.sampleM()" value="ok" />
</h:form>

现在,我想以编程方式向此表单发送 POST 请求。

根据我的调查,这里的关键是 POST 参数。 正确选择会给出正确的结果(字符串“测试:文本测试”打印在服务器的控制台上)。

所以问题是:我应该如何选择正确的 POST 数据?

上面显示的 JSF 表单生成这个 html 表单:

<form id="sampleForm" name="sampleForm" method="post" action="/pages/main/main.smnet" enctype="application/x-www-form-urlencoded">
    <input type="hidden" name="sampleForm" value="sampleForm" />
    <input id="sampleForm:sampleButton" type="submit" name="sampleForm:sampleButton" value="ok" />
    <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id65" autocomplete="off" />
</form>

所以这些参数是正确的。

但我怎样才能找出哪些参数(名称和值)足以满足任何其他组件的需求?

例如:当我发送与显示的 HTML 表单相同的 POST 数据但具有不同的 'javax.faces.ViewState' 参数值时,不会执行组件方法。

【问题讨论】:

【参考方案1】:

我知道您基本上是在询问如何使用某些 HTTP 客户端(例如 java.net.URLConnection 或 Apache HttpComponents 客户端)以编程方式提交 JSF 表单,对吧?

您需要先发送一个 GET 请求,并确保在请求之间保持相同的 HTTP 会话(基本上是JSESSIONID cookie)。让您的 HTTP 客户端从第一个 GET 请求的响应中提取 Set-Cookie 标头,从中获取 JSESSIONID cookie 并将其作为后续 POST 请求的 Cookie 标头发送回。这将在服务器端维护 HTTP 会话,否则 JSF 会将其视为“视图已过期”,这可能会在正确配置的 JSF Web 应用程序上返回带有 ViewExpiredException 的 HTTP 500 错误页面,或者在配置错误的 JSF Web 上返回应用程序表现为页面刷新。

作为 JSF 的有状态特性和隐含的 CSRF 攻击防护的一部分,必须使用有效的 javax.faces.ViewState 值提交表单,因为客户端已在初始 GET 请求中检索到自己。您还需要确保发送所有其他隐藏字段的 name=value 对,尤其是提交按钮之一。

所以,如果您的初始 GET 请求返回此 HTML

<form id="sampleForm" name="sampleForm" method="post" action="/pages/main/main.smnet" enctype="application/x-www-form-urlencoded">
    <input type="hidden" name="sampleForm" value="sampleForm" />
    <input id="sampleForm:sampleButton" type="submit" name="sampleForm:sampleButton" value="ok" />
    <input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="j_id65" autocomplete="off" />
</form>

然后你需要解析它(Jsoup可能对此有帮助)并提取以下请求参数:

sampleForm=sampleForm sampleForm:sampleButton=ok javax.faces.ViewState=j_id65

最后在/pages/main/main.smnet 上发送一个带有这些请求参数的POST 请求(以及JSESSIONID cookie!)。不过要小心,一个(可怜的)JSF 开发人员可能已经跳过了,例如id="sampleButton" 来自 &lt;h:commandButton&gt;,然后 JSF 会自动生成一个看起来像这种格式 sampleForm:j_id42 的文件。您不能对它们进行硬编码,因为值可能会根据组件在服务器端树中的位置而改变,然后您真的需要将其解析出获得的 HTML。

不过,明智的做法是联系网站所有者/管理员,并询问是否没有可用于您心目中的任务的 Web 服务 API。一个体面的 Java EE 网站使用 JSF 应用程序作为 HTML 前端通常也使用单独的 JAX-RS 应用程序作为 REST 前端。通过这样的 Web 服务 API 提取信息比通过抓取 HTML 文档要容易和可靠得多。

另见:

How can i programmatically upload a file to a website?(这也涉及 JSF) How to use java.net.URLConnection to fire and handle HTTP requests

【讨论】:

基本上我想在不使用 HTML 表单和 HTML 页面的情况下访问 JSF 组件和 JSF 生命周期。可能吗?假设JSESSIONID 是已知的。我可以以某种方式向服务器询问有效的javax.faces.ViewState 值吗?也许还有其他方法可以访问 JSF 组件? 没有。 JSF 是一个基于 Web 的 MVC 框架,旨在生成基于 HTML 表单的应用程序,它不是一个 Web 服务框架。如果该 JSF 网站在您的完全控制之下,那么您基本上是在使用错误的工具来完成这项工作。改为创建一个 Web 服务(您甚至可以与 JSF 一起运行它)。 Java EE API 为此提供了 JAX-RS (RESTful) 和 JAX-WS (SOAP) API。如果该 JSF 网站不受您的完全控制,如果没有可用的网络服务,请联系管理员。 所以没有办法生成这样的场景:我有一个 JSF 应用程序和几个 Web 服务(都在一个应用程序的同一台服务器上)。当请求到达webservice方法时,该方法使用存储在JSFapplication scope中的对象(无需查询数据库)? JSF 应用范围在ServletContext 的属性所代表的幕后。如果 web 服务在同一个 webapp 和容器中运行,它肯定可以访问同一个 ServletContext 实例,因此也可以访问它的所有属性。 请@balusC,你能回答这个问题吗***.com/questions/23503948/…

以上是关于如何在不使用 HTML 表单的情况下以编程方式将 POST 请求发送到 JSF 页面?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 UINavigationController 的情况下以编程方式进入 rootViewController

如何在不使用命令的情况下以编程方式关闭/重启 linux 机器(运行时)

如何在不使用外部主机的情况下以编程方式查找设备的外部 IP 地址?

如何在不使用第三方的情况下以编程方式从 Chase 下载我的银行交易?

如何在不使用 Facebook 登录/注销按钮的情况下以编程方式从 Facebook SDK 3.0 注销?

如何在不使用 rest admin api 的情况下以编程方式(java)更新 keycloak 的用户详细信息?