How to programmatically send POST request to JSF page without using HTML form?

pWoz picture pWoz · Aug 29, 2012 · Viewed 22.5k times · Source

I have very simple JSF bean like shown below:

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;
    }
}

And JSF form connected with this component:

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

Now, I would like to programmatically send POST request to this form.

According to my investigation the key here are POST parameters. Selected properly gives proper results (String 'Test: text-test' is printed on serwer's console).

So the question is: How should I select POST data that was correct?

JSF form shown above produces this HTML form:

<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>

So these parameters are corrent.

But how can I find out what parameters (name and value) will be sufficient for any other component?

For example: when I send POST data the same like in shown HTML form but with different 'javax.faces.ViewState' parameter value, component method will not be executed.

Answer

BalusC picture BalusC · Aug 29, 2012

I understand that you're basically asking how to submit a JSF form programmatically using some HTTP client such as java.net.URLConnection or Apache HttpComponents Client, right?

You need to send a GET request first and make sure that you maintain the same HTTP session (basically, the JSESSIONID cookie) across requests. Let your HTTP client extract the Set-Cookie header from the response of the first GET request, obtain the JSESSIONID cookie from it and send it back as Cookie header of subsequent POST requests. This will maintain the HTTP session in the server side, otherwise JSF will treat it as a "View Expired" which may return either on a decently configured JSF web application a HTTP 500 error page with ViewExpiredException, or on a badly configured JSF web application behave as a page refresh.

As part of JSF's stateful nature and implied CSRF attack prevention, the forms must be submitted with a valid javax.faces.ViewState value as the client has retrieved itself on the initial GET request. You also need to make sure that you send the name=value pair of all other hidden fields and particularly the one of the submit button along as well.

So, if your initial GET request gives you this HTML back

<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>

then you need to parse it (Jsoup may be helpful in this) and extract the following request parameters:

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

Finally send a POST request on /pages/main/main.smnet with exactly those request parameters (and the JSESSIONID cookie!). Be careful though, it's possible that a (poor) JSF developer has skipped e.g. id="sampleButton" from the <h:commandButton> and then JSF would autogenerate one which looks like in this format sampleForm:j_id42. You can't hardcode them as the value may change depending on the component's position in the server side tree and you would then really need to parse it out the obtained HTML.

Nonetheless, it's wise to contact the site owner/admin and ask if there isn't a web service API available for the task you had in mind. A decent Java EE website which uses a JSF application for a HTML frontend usually also uses a separate JAX-RS application for a REST frontend.

See also: